GPU程式設計(五):利用好sharedmemory
目錄
- 前言
- CPU矩陣轉置
- GPU實現
- 簡單移植
- 單block
- tile
- 利用率計算
- shared memory
- 最後
前言
之前在第三章對比過CPU和GPU, 差距非常大. 這一次來看看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( "
" );
}
}
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
", timeuse );
#ifdef LOG
logM( in );
printf( "
" );
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
", timeuse );
#ifdef LOG
logM( in );
printf( "
" );
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 rate和Memory Bus Width是900Mhz和128-bit, 所以峰值就是14.4GB/s.
之前的最短耗時是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%, 及格了.
所以這就是依據架構來設計演算法, 回顧一下架構圖:
最後
但是44%也就是達到了及格線, 也就是說, 還有更深層次的優化工作需要做. 這些內容也就放在後續文章中了, 有意見或者建議評論區見~
相關文章
- GPU程式設計(五): 利用好shared memoryGPU程式設計
- GPU程式設計--CPU和GPU的設計區別GPU程式設計
- cuda程式設計與gpu平行計算(四):cuda程式設計模型程式設計GPU模型
- shell程式設計五程式設計
- MFC程式設計(五)C程式程式設計
- GPU程式設計--OpenCL四大模型GPU程式設計大模型
- 程式設計實驗五程式設計
- 五種Java程式設計高效程式設計方法 - BablaJava程式設計
- Java中神經網路Triton GPU程式設計Java神經網路GPU程式設計
- GPU高效能程式設計CUDA實戰(二)GPU程式設計
- GPU程式設計(四):並行規約優化GPU程式設計並行優化
- 第五講 TCP程式設計TCP程式設計
- 結對程式設計的利與弊程式設計
- GPU精粹與Shader程式設計(四):真實感渲染GPU程式設計
- GPU程式設計(零):老黃和他的核彈們GPU程式設計
- 程式設計師修煉之道~五程式設計師
- 每週五程式設計師段子程式設計師
- 趕緊收藏!程式設計師必備的工具網站,用好了節省你大把的程式設計時間程式設計師網站
- 如何順利通過程式設計面試程式設計面試
- 如何從初級程式設計師順利晉升到高階程式設計師?程式設計師
- cuda程式設計與gpu平行計算(六):圖稀疏矩陣轉為CSR結構並傳入gpu程式設計GPU矩陣
- 世界五大計算機程式設計師計算機程式設計師
- 五、GO程式設計模式:MAP-REDUCEGo程式設計設計模式
- GPU精粹與Shader程式設計(一):關於基礎物理渲染GPU程式設計
- GPU程式設計(一):Ubuntu下的CUDA8.0環境搭建GPU程式設計Ubuntu
- 五軸電火花穿孔CAM程式設計軟體程式設計
- 美國五大高薪程式設計工作排名高薪程式設計
- ~~核心程式設計(五):物件導向——多繼承~~程式設計物件繼承
- 程式設計師必收藏的五個網站程式設計師網站
- 五分鐘自學程式設計:程式設計師到底怎麼學資料結構?!程式設計師資料結構
- GPU精粹與Shader程式設計(二):次表面散射與環境光遮蔽GPU程式設計
- Go語言程式設計有哪些利與弊?程式設計時如何判斷是否應該用Go?Go程式設計
- 資料庫實驗五:資料庫程式設計資料庫程式設計
- 五年程式設計師記流水賬式的自白。程式設計師
- 詳解MapReduce中的五大程式設計模型程式設計模型
- 彙編實驗小記(五)-迴圈程式設計程式設計
- 五分鐘學習 Java 8 的流程式設計Java程式設計
- java面試-Java併發程式設計(五)——中斷Java面試程式設計