博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GPU编程(五): 利用好shared memory
阅读量:6834 次
发布时间:2019-06-26

本文共 4387 字,大约阅读时间需要 14 分钟。

目录

  • 前言
  • CPU矩阵转置
  • GPU实现
  • 简单移植
  • 单block
  • tile
  • 利用率计算
  • shared memory
  • 最后

前言

之前在第三章对比过CPU和GPU, 差距非常大. 这一次来看看GPU自身的优化, 主要是shared memory的用法.


CPU矩阵转置

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

#include 
#include
#include
#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 
#include
#include
#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存储架构


最后

但是44%也就是达到了及格线, 也就是说, 还有更深层次的优化工作需要做. 这些内容也就放在后续文章中了, 有意见或者建议评论区见~


转载地址:http://spxkl.baihongyu.com/

你可能感兴趣的文章
手动构建CL210环境——packstack部署vlan模式
查看>>
随机生成UserAgent的python库(fake-useragent库)
查看>>
HTML5 标签、事件句柄属性以及浏览器兼容情况速查手册
查看>>
NSMutableArray可变数组
查看>>
Missing value auth-url required for auth plugin password
查看>>
PowerShell获取服务器本地服务状态
查看>>
禁止弹出输入法
查看>>
rsync 文件同步 服务器和客户端配置
查看>>
关于jvm的读书笔记-性能监控工具
查看>>
FlipViewPager 对item实现左右对折滑动翻页效果《IT蓝豹》
查看>>
Leetcode日记5
查看>>
时间:2014年4月11日22:15:47 session 概念
查看>>
我的友情链接
查看>>
教育“优先”,落实才是关键
查看>>
传统IT大佬们,路在何方?
查看>>
基础练习
查看>>
shell学习笔记 (9.3)
查看>>
用chrome在电脑上模拟微信内置浏览器
查看>>
PHP获取常用时间的总结
查看>>
设计模式6大原则:里氏置换原则
查看>>