第五篇:CUDA 並行程序中的同步


前言

       在並發,多線程環境下,同步是一個很重要的環節。同步即是指進程/線程之間的執行順序約定。

       本文將介紹如何通過共享內存機制實現塊內多線程之間的同步。

       至於塊之間的同步,需要使用到 global memory,代價較為高昂,目前使用的情況也不多,就先不介紹了。

塊內同步函數:__syncthreads ()

       線程調用此函數后,該線程所屬塊中的所有線程均運行到這個調用點后才會繼續往下運行。

代碼示例

       使用同步思想優化之前一篇博文中提到的數組求和程序。在新的程序中,讓每個塊中的第一個線程將塊中所有線程的運算結果都加起來,然后再存入到結果數組中。這樣,結果數組的長度與塊數相等 (原來是和總線程數相等),大大降低了 CPU 端程序求和的工作量以及需要傳遞進/出顯存的數據 (代碼下方如果出現紅色波浪線無視之):

  1 // 相關 CUDA 庫
  2 #include "cuda_runtime.h"
  3 #include "cuda.h"
  4 #include "device_launch_parameters.h"
  5 
  6 // 此頭文件包含 __syncthreads ()函數
  7 #include "device_functions.h"
  8 
  9 #include <iostream>
 10 #include <cstdlib>
 11 
 12 using namespace std;
 13 
 14 const int N = 100;
 15 
 16 // 塊數
 17 const int BLOCK_data = 3; 
 18 // 各塊中的線程數
 19 const int THREAD_data = 10; 
 20 
 21 // CUDA初始化函數
 22 bool InitCUDA()
 23 {
 24     int deviceCount; 
 25 
 26     // 獲取顯示設備數
 27     cudaGetDeviceCount (&deviceCount);
 28 
 29     if (deviceCount == 0) 
 30     {
 31         cout << "找不到設備" << endl;
 32         return EXIT_FAILURE;
 33     }
 34 
 35     int i;
 36     for (i=0; i<deviceCount; i++)
 37     {
 38         cudaDeviceProp prop;
 39         if (cudaGetDeviceProperties(&prop,i)==cudaSuccess) // 獲取設備屬性
 40         {
 41             if (prop.major>=1) //cuda計算能力
 42             {
 43                 break;
 44             }
 45         }
 46     }
 47 
 48     if (i==deviceCount)
 49     {
 50         cout << "找不到支持 CUDA 計算的設備" << endl;
 51         return EXIT_FAILURE;
 52     }
 53 
 54     cudaSetDevice(i); // 選定使用的顯示設備
 55 
 56     return EXIT_SUCCESS;
 57 }
 58 
 59 // 此函數在主機端調用,設備端執行。
 60 __global__ 
 61 static void Sum (int *data,int *result)
 62 {
 63     // 聲明共享內存 (數組)
 64     extern __shared__ int shared[]; 
 65     // 取得線程號
 66     const int tid = threadIdx.x; 
 67     // 獲得塊號
 68     const int bid = blockIdx.x; 
 69 
 70     shared[tid] = 0;
 71     // 有點像網格計算的思路
 72     for (int i=bid*THREAD_data+tid; i<N; i+=BLOCK_data*THREAD_data)
 73     {
 74         shared[tid] += data[i];
 75     }
 76    
 77     // 塊內線程同步函數
 78     __syncthreads ();
 79 
 80     // 每個塊內索引為 0 的線程對其組內所有線程的求和結果再次求和
 81     if (tid == 0) {
 82         for(int i = 1; i < THREAD_data; i++) { 
 83             shared[0] += shared[i];
 84         }
 85         // result 數組存放各個塊的計算結果
 86         result[bid] = shared[0];
 87     }
 88 }
 89 
 90 int main ()
 91 {
 92     // 初始化 CUDA 編譯環境
 93     if (InitCUDA()) {
 94         return EXIT_FAILURE;
 95     }
 96     cout << "成功建立 CUDA 計算環境" << endl << endl;
 97 
 98     // 建立,初始化,打印測試數組
 99     int *data = new int [N];
100     cout << "測試矩陣: " << endl;
101     for (int i=0; i<N; i++)
102     {
103         data[i] = rand()%10;
104         cout << data[i] << " ";
105         if ((i+1)%10 == 0) cout << endl;
106     }
107     cout << endl;
108 
109     int *gpudata, *result; 
110     
111     // 在顯存中為計算對象開辟空間
112     cudaMalloc ((void**)&gpudata, sizeof(int)*N); 
113     // 在顯存中為結果對象開辟空間
114     cudaMalloc ((void**)&result, sizeof(int)*BLOCK_data);
115     
116     // 將數組數據傳輸進顯存
117     cudaMemcpy (gpudata, data, sizeof(int)*N, cudaMemcpyHostToDevice); 
118     // 調用 kernel 函數 - 此函數可以根據顯存地址以及自身的塊號,線程號處理數據。
119     Sum<<<BLOCK_data,THREAD_data,THREAD_data*sizeof (int)>>> (gpudata,result);
120     
121     // 在內存中為計算對象開辟空間
122     int *sumArray = new int[BLOCK_data];
123     // 從顯存獲取處理的結果
124     cudaMemcpy (sumArray, result, sizeof(int)*BLOCK_data, cudaMemcpyDeviceToHost);
125     
126     // 釋放顯存
127     cudaFree (gpudata); 
128     cudaFree (result);
129 
130     // 計算 GPU 每個塊計算出來和的總和
131     int final_sum=0;
132     for (int i=0; i<BLOCK_data; i++)
133     {
134         final_sum += sumArray[i];
135     }
136 
137     cout << "GPU 求和結果為: " << final_sum << endl;
138 
139     // 使用 CPU 對矩陣進行求和並將結果對照
140     final_sum = 0;
141     for (int i=0; i<N; i++)
142     {
143         final_sum += data[i];
144     }
145     cout << "CPU 求和結果為: " << final_sum << endl;
146 
147     getchar();
148 
149     return 0;
150 }

運行結果

  

  PS:矩陣元素是隨機生成的

小結

  共享內存,或者說這個共享數組是 CUDA 中實現同步最常用的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM