云优惠
 云服务器优惠大全

首页    编程语言    GPU编程: 怎么利用好shared memory

GPU编程: 怎么利用好shared memory

创建时间:2019-02-20 10:00
浏览量:0
收藏

前言

这一次来看看GPU自身的优化, 主要是shared memory的用法.


CPU矩阵转置

矩阵转置不是什么复杂的事情. 用CPU实现是很简单的:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define LOG_
#define N 1024

/* 转置 */
void transposeCPU( float in[], float out[] )
{
    for ( int j = 0; j < N; j++ )
    {
        for ( int i = 0; i < N; i++ )
        {
            out[j * N + i] = in[i * N + j];
        }
    }
}


/* 打印矩阵 */
void logM( float m[] )
{
    for ( int i = 0; i < N; i++ )
    {
        for ( int j = 0; j < N; j++ )
        {
            printf( "%.1f ", m[i * N + j] );
        }
        printf( "\n" );
    }
}


int main()
{
    int    size    = N * N * sizeof(float);
    float    *in    = (float *) malloc( size );
    float    *out    = (float *) malloc( size );

    /* 矩阵赋值 */
    for ( int i = 0; i < N; ++i )
    {
        for ( int j = 0; j < N; ++j )
        {
            in[i * N + j] = i * N + j;
        }
    }

    struct timeval    start, end;
    double        timeuse;
    int        sum = 0;
    gettimeofday( &start, NULL );

    transposeCPU( in, out );

    gettimeofday( &end, NULL );
    timeuse = end.tv_sec - start.tv_sec + (end.tv_usec - start.tv_usec) / 1000000.0;
    printf( "Use Time: %fs\n", timeuse );

#ifdef LOG
    logM( in );
    printf( "\n" );
    logM( out );
#endif

    free( in );
    free( out );
    return(0);
}

GPU实现

简单移植

如果什么都不考虑, 只是把代码移植到GPU:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define N 1024
#define LOG_

/* 转置 */
__global__ void transposeSerial( float in[], float out[] )
{
    for ( int j = 0; j < N; j++ )
        for ( int i = 0; i < N; i++ )
            out[j * N + i] = in[i * N + j];
}

/* 打印矩阵 */
void logM( float m[] ){...}

int main()
{
    int size = N * N * sizeof(float);

    float *in, *out;

    cudaMallocManaged( &in, size );
    cudaMallocManaged( &out, size );

    for ( int i = 0; i < N; ++i )
        for ( int j = 0; j < N; ++j )
            in[i * N + j] = i * N + j;

    struct timeval    start, end;
    double        timeuse;
    gettimeofday( &start, NULL );

    transposeSerial << < 1, 1 >> > (in, out);

    cudaDeviceSynchronize();

    gettimeofday( &end, NULL );
    timeuse = end.tv_sec - start.tv_sec + (end.tv_usec - start.tv_usec) / 1000000.0;
    printf( "Use Time: %fs\n", timeuse );


#ifdef LOG
    logM( in );
    printf( "\n" );
    logM( out );
#endif

    cudaFree( in );
    cudaFree( out );
}

不用想, 这里肯定是还不如单线程的CPU的, 真的是完完全全的资源浪费. 实测下来, 耗时是CPU的20多倍, 大写的丢人.

耗时

单block

单block最多可以开1024线程, 这里就开1024线程跑下.

/* 转置 */
__global__ void transposeParallelPerRow( float in[], float out[] )
{
    int i = threadIdx.x;
    for ( int j = 0; j < N; j++ )
        out[j * N + i] = in[i * N + j];
}

int main()
{
    ...
    transposeParallelPerRow << < 1, N >> > (in, out);
    ...
}

效率一下就提升了, 耗时大幅下降.

耗时

tile

但是的话, 如果可以利用多个block, 把矩阵切成更多的tile, 效率还会进一步提升.

/* 转置 */
__global__ void transposeParallelPerElement( float in[], float out[] )
{
    int i = blockIdx.x * K + threadIdx.x;
    /* column */
    int j = blockIdx.y * K + threadIdx.y;
    /* row */
    out[j * N + i] = in[i * N + j];
}

int main()
{
    ...
    dim3 blocks( N / K, N / K );
    dim3 threads( K, K );

    ...
    
    transposeParallelPerElement << < blocks, threads >> > (in, out);
    ...
}

这些都是GPU的常规操作, 但其实利用率依旧是有限的.

耗时


利用率计算

利用率是可以粗略计算的, 比方说, 这里的Memory Clock rateMemory Bus Width是900Mhz和128-bit, 所以峰值就是14.4GB/s.

GPU参数

之前的最短耗时是0.001681s. 数据量是1024*1024*4(Byte)*2(读写). 所以是4.65GB/s. 利用率就是32%. 如果40%算及格, 这个利用率还是不及格的.


shared memory

那该如何提升呢? 问题在于读数据的时候是连着读的, 一个warp读32个数据, 可以同步操作, 但是写的时候就是散开来写的, 有一个很大的步长. 这就导致了效率下降. 所以需要借助shared memory, 由他转置数据, 这样, 写入的时候也是连续高效的了.

/* 转置 */
__global__ void transposeParallelPerElementTiled( float in[], float out[] )
{
    int    in_corner_i    = blockIdx.x * K, in_corner_j = blockIdx.y * K;
    int    out_corner_i    = blockIdx.y * K, out_corner_j = blockIdx.x * K;

    int x = threadIdx.x, y = threadIdx.y;

    __shared__ float tile[K][K];

    tile[y][x] = in[(in_corner_i + x) + (in_corner_j + y) * N];
    __syncthreads();
    out[(out_corner_i + x) + (out_corner_j + y) * N] = tile[x][y];
}

int main()
{

    ...
    dim3 blocks( N / K, N / K );
    dim3 threads( K, K );

    struct timeval    start, end;
    double        timeuse;
    gettimeofday( &start, NULL );

    transposeParallelPerElementTiled << < blocks, threads >> > (in, out);
    ...

}

这样利用率就来到了44%, 及格了.

耗时

所以这就是依据架构来设计算法, 回顾一下架构图:

GPU存储架构


 

免费领取阿里云1888元代金券大礼包

 

阿里云新老用户均可领取!
自领取后:限时7天使用!

阿里云服务器2折优惠:低至293元/年

 

 

突发性能实例t5 1核1G:293元/年

突发性能实例t5 1核2G:459元/年

突发性能实例t5 2核4G:798元/年

共享型xn4实例1核1G内存:394元/年

共享型n4实例1核2G内存:653元/年

计算网络增强型实例2核4G内存:1566元/年

计算网络增强型实例4核8G内存:2991元/年

点此查看2折活动详情

阿里云高性能云服务器

 

 

网络增强型云服务器:2核4G ¥720元/年

高频应用云服务器:8核16G ¥4109元/年

本地SSD型云服务器:4核16G ¥6218.40元/年

大数据型云服务器:8核32G ¥11375.00元/年

GPU异构云服务器:16核40G ¥15563.00元/年

新用户满立减:每满1000立减50

 

1、到阿里云官网选购产品
2、加入到购物车
3、结算时立享满减

注意:新用户首次购买时必须先加到购物车,然后一起结算才享受此优惠。

腾讯云CVM云服务器22.07元起

 

 

腾讯云1核1G:22.07元/月、794.73元/3年

腾讯云2核2G:36.48元/月、1313.35元/3年

腾讯云2核4G:43.01元/月、1548.5元/3年

腾讯云4核8G:178.5元/月、6426元/3年