GPU程式設計(四):並行規約優化
目錄
- 前言
- cuda-gdb
- 未優化並行規約
- 優化後並行規約
- 結果分析
- 最後
前言
- 之前第三篇也看到了, 並行方面GPU真的是無往不利, 現在再看下第二個例子, 並行規約. 通過這次的例子會發現, 需要了解GPU架構, 然後寫出與之對應的演算法的, 兩者結合才能得到令人驚歎的結果.
- 這次也會簡要介紹下cuda-gdb的用法, 其實和gdb用法幾乎一樣, 也就是多了個cuda命令.
cuda-gdb
如果之前沒有用過gdb, 可以速學一下, 就幾個指令.
想要用cuda-gdb對程式進行除錯, 首先你要確保你的gpu沒有在執行作業系統介面, 比方說, 我用的是ubuntu, 我就需要用sudo service lightdm stop
關閉圖形介面, 進入tty1這種字元介面.
當然用ssh遠端訪問也是可以的.
接下來, 使用第二篇中矩陣加法的例子. 但是注意, 編譯的使用需要改變一下, 加入-g -G引數, 其實和gdb是相似的.
nvcc -g -G CUDAAdd.cu -o CUDAAdd.o
然後使用
cuda-gdb CUDAAdd.o
即可對程式進行除錯.
在除錯之前, 我把程式碼貼出來:
#include <stdio.h>
__global__ void add(float * x, float *y, float * z, int n){
int index = threadIdx.x + blockIdx.x * blockDim.x;
int stride = blockDim.x * gridDim.x;
for (int i = index; i < n; i += stride){
z[i] = x[i] + y[i];
}
}
int main()
{
int N = 1 << 20;
int nBytes = N * sizeof(float);
float *x, *y, *z;
cudaMallocManaged((void**)&x, nBytes);
cudaMallocManaged((void**)&y, nBytes);
cudaMallocManaged((void**)&z, nBytes);
for (int i = 0; i < N; ++i)
{
x[i] = 10.0;
y[i] = 20.0;
}
dim3 blockSize(256);
// 4096
dim3 gridSize((N + blockSize.x - 1) / blockSize.x);
add << < gridSize, blockSize >> >(x, y, z, N);
cudaDeviceSynchronize();
float maxError = 0.0;
for (int i = 0; i < N; i++){
maxError = fmax(maxError, (float)(fabs(z[i] - 30.0)));
}
printf ("max default: %.4f
", maxError);
cudaFree(x);
cudaFree(y);
cudaFree(z);
return 0;
}
之後就是常規操作了, 新增斷點, 執行, 下一步, 檢視想看的資料. 不同點是cuda的指令, 例如
cuda block(1,0,0)
可以從一開始block(0,0,0)切換到block(1,0,0).
未優化並行規約
如果按照常規的思路, 兩兩進行進行加法運算. 每次步長翻倍即可, 從演算法的角度來說, 這是沒啥問題的. 但是沒有依照GPU架構進行設計.
#include <stdio.h>
const int threadsPerBlock = 512;
const int N = 2048;
const int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock; /* 4 */
__global__ void ReductionSum( float * d_a, float * d_partial_sum )
{
/* 申請共享記憶體, 存在於每個block中 */
__shared__ float partialSum[threadsPerBlock];
/* 確定索引 */
int i = threadIdx.x + blockIdx.x * blockDim.x;
int tid = threadIdx.x;
/* 傳global memory資料到shared memory */
partialSum[tid] = d_a[i];
/* 傳輸同步 */
__syncthreads();
/* 在共享儲存器中進行規約 */
for ( int stride = 1; stride < blockDim.x; stride *= 2 )
{
if ( tid % (2 * stride) == 0 )
partialSum[tid] += partialSum[tid + stride];
__syncthreads();
}
/* 將當前block的計算結果寫回輸出陣列 */
if ( tid == 0 )
d_partial_sum[blockIdx.x] = partialSum[0];
}
int main()
{
int size = sizeof(float);
/* 分配視訊記憶體空間 */
float * d_a;
float * d_partial_sum;
cudaMallocManaged( (void * *) &d_a, N * size );
cudaMallocManaged( (void * *) &d_partial_sum, blocksPerGrid * size );
for ( int i = 0; i < N; ++i )
d_a[i] = i;
/* 呼叫核心函式 */
ReductionSum << < blocksPerGrid, threadsPerBlock >> > (d_a, d_partial_sum);
cudaDeviceSynchronize();
/* 將部分和求和 */
int sum = 0;
for ( int i = 0; i < blocksPerGrid; ++i )
sum += d_partial_sum[i];
printf( "sum = %d
", sum );
/* 釋放視訊記憶體空間 */
cudaFree( d_a );
cudaFree( d_partial_sum );
return(0);
}
優化後並行規約
其實需要改動的地方非常小, 改變步長即可.
__global__ void ReductionSum( float * d_a, float * d_partial_sum )
{
// 相同, 略去
/* 在共享儲存器中進行規約 */
for ( int stride = blockDim.x / 2; stride > 0; stride /= 2 )
{
if ( tid < stride )
partialSum[tid] += partialSum[tid + stride];
__syncthreads();
}
// 相同, 略去
}
結果分析
之前的文章裡面也說過warp.
warp: GPU執行程式時的排程單位, 目前cuda的warp的大小為32, 同在一個warp的執行緒, 以不同資料資源執行相同的指令, 這就是所謂SIMT.
說人話就是, 這32個執行緒必須要幹相同的事情, 如果有執行緒動作不一致, 就需要等待一波執行緒完成自己的工作, 然後再去做另外一件事情.
所以, 用圖說話就是, 第二種方案可以更快將warp閒置, 交給GPU排程, 所以, 肯定是第二種更快.
圖一在運算依次之後, 沒有warp可以空閒, 而圖二直接空閒2個warp. 圖一到了第二次可以空閒2個warp, 而圖二已經空閒3個warp. 我這副圖只是示意圖, 如果是實際的, 差距會更大.
所以來看下執行耗時, 會發現差距還是很大的, 幾乎是差了一倍. 不過GPU確實算力太猛, 這樣看還不太明顯, 有意放大資料量會更加明顯.
最後
所以GPU又一次展示了強大的算力, 而且, 這次也看到了只是小小變動, 讓演算法更貼合架構, 就讓運算耗時減半, 所以在優化方面可以做的工作真的是太多了, 之後還有更多優化相關的文章, 有意見或者建議, 評論區見哦~
相關文章
- GPU程式設計--OpenCL四大模型GPU程式設計大模型
- cuda程式設計與gpu平行計算(四):cuda程式設計模型程式設計GPU模型
- 鮑勃大叔:程式設計正規化並不排斥!程式設計
- 第四篇:GPU 並行程式設計的儲存系統架構GPU並行行程程式設計架構
- Metal:對 iOS 中 GPU 程式設計的高度優化的框架iOSGPU程式設計優化框架
- 第六篇:GPU 並行優化的幾種典型策略GPU並行優化
- C#多執行緒(四)並行程式設計篇之結構化C#執行緒並行行程程式設計
- 多流向演算法GPU並行化演算法GPU並行
- 第五篇:淺談CPU 並行程式設計和 GPU 並行程式設計的區別並行行程程式設計GPU
- GPU程式設計--CPU和GPU的設計區別GPU程式設計
- java程式設計規約----程式碼風格(一)Java程式設計
- GPU精粹與Shader程式設計(四):真實感渲染GPU程式設計
- Java多執行緒程式設計—鎖優化Java執行緒程式設計優化
- 程式分析與優化 - 10 指令級並行優化並行
- 資料庫設計正規化2——BC正規化和第四正規化資料庫
- C++實戰之OpenCL 並行優化程式設計從零學起系列文章C++並行優化程式設計
- 使用執行緒池優化多執行緒程式設計執行緒優化程式設計
- cuda程式設計與gpu平行計算(六):圖稀疏矩陣轉為CSR結構並傳入gpu程式設計GPU矩陣
- 第三篇:GPU 並行程式設計的運算架構GPU並行行程程式設計架構
- shell程式設計(四)| grep正規表示式程式設計
- shell程式設計(四)| sed 正規表示式程式設計
- 淺談程式設計正規化程式設計
- JavaScript模組化程式設計規範JavaScript程式設計
- java優化程式設計-物件重用Java優化程式設計物件
- 淺談GPU虛擬化技術(四)-GPU分片虛擬化GPU
- 淺談GPU虛擬化技術(四)- GPU分片虛擬化GPU
- 程式設計模型(正規化)小結程式設計模型
- (Python程式設計 | 系統程式設計 | 並行系統工具 | 程式退出)Python程式設計並行
- OpenMP並行化例項----Mandelbrot集合並行化計算並行
- 程式設計正規化 —— 函數語言程式設計入門程式設計函數
- GPU程式設計入門(8) GPU ASM 頂點渲染初步GPU程式設計ASM
- 升級 Java 程式設計規範的6個約定Java程式設計
- .NET併發程式設計-資料並行程式設計並行
- 簡約至上(互動設計四策略)
- 和程式設計師約會的優點和缺點程式設計師
- GPU程式設計(五):利用好sharedmemoryGPU程式設計
- 程式設計正規化(Programmingparadigm)程式設計
- JavaScript 模組化程式設計(二):AMD規範JavaScript程式設計