GPU可以利用平行處理的方式,縮短執行時間,因此,這一次就來介紹多執行緒的程式設計方法及應用。
上一篇 介紹GPU函數的設定是透過<<<...>>>指定三個參數:
每一種GPU的數量均不相同,因此,我們先要偵測相關的數量限制,可以執行上一篇的deviceQuery.exe,預設建置的目錄在【v11.2\bin\win64\Debug】。
所以,如果要在不同的卡都能執行,必須在程式中計算可設定的執行緒數量,總數量超過程式就會發生異常。共享記憶體也是一樣的道理。【GTX1050 Ti】含6個處理器,我在以下的程式設定7*2048個執行緒也不會發生異常,我猜處理器有類似queue的功能,一旦執行緒超過容量限制,工作會排隊等待,直到處理器有空時才被處理。
接下來,我們使用多執行緒進行向量的相加,一維陣列中的每個元素都獨立計算,每個元素計算交由一個執行緒處理。
#define N 1024
__global__ void vectorAdd(int* d_a, int* d_b, int* d_c) {
// 使用第幾個執行緒計算
int tid = threadIdx.x;
//printf("threadIdx = %d\n", tid);
d_c[tid] = d_a[tid] + d_b[tid];
}
int h_a[N], h_b[N], h_c[N], h2_c[N];
// 設定 a、b 的值
for (int i = 0; i < N; i++) {
h_a[i] = i; // 1,...,N
h_b[i] = i * 2; // 2,...,2xN
}
// 分配記憶體
cudaMalloc((void**)&d_a, N * sizeof(int));
// 自 CPU變數 複製到 GPU變數
cudaMemcpy(d_a, h_a, N * sizeof(int), cudaMemcpyHostToDevice);
cudaMalloc((void**)&d_b, N * sizeof(int));
cudaMemcpy(d_b, h_b, N * sizeof(int), cudaMemcpyHostToDevice);
// 分配記憶體
cudaMalloc((void**) &d_c, N * sizeof(int));
// 使用GPU進行向量相加,1 block x N threads
vectorAdd <<<1, N >>> (d_a, d_b, d_c);
// 等待所有執行緒處理完畢
cudaDeviceSynchronize();
cudaFree(d_a);
cudaFree(d_b);
cudaFree(d_c);
vectorAdd <<<2, 1024 >>> (d_a, d_b, d_c);
vectorAdd 函數的陣列索引值要改為:
// blockIdx.x:第幾個block,blockDim.x:block內的執行緒數量
int tid = threadIdx.x + blockIdx.x * blockDim.x;
在 GPU 撰寫多執行緒真是方便,只要呼叫時,指定個數即可,但是,要以程式檢查是否超出硬體限制條件,這部分可自deviceQuery截取相關程式碼,計算應使用的區塊數及執行緒數量。
平行處理要選擇多區塊或多執行緒呢? 使用多執行緒有兩個好處:
完整程式放在 『GitHub』的VectorAdd目錄。使用多個區塊的改良程式放在VectorAdd_Improved目錄。