第六篇:二維陣列的傳輸 (host <-> device)

穆晨發表於2017-01-19

前言

       本文的目的很明確:介紹如何將二維陣列傳遞進視訊記憶體,以及如何將二維陣列從視訊記憶體傳遞迴主機端。

實現步驟

       1. 在視訊記憶體中為二維陣列開闢空間

       2. 獲取該二維陣列在視訊記憶體中的 pitch 值 (cudaMallocPitch 實現)

       3. 將二維陣列傳遞進視訊記憶體 (cudaMemcpy2D 實現)

       4. 在視訊記憶體中對該二維陣列進行處理 (目前必須按照 1 維陣列的規則進行處理)

       5. 將結果傳遞迴記憶體 (cudaMemcpy2D實現)

重要概念 - pitch

       對於記憶體的存取來說,對準偏移量為2的冪(現在一般要求2^4=16)的地址能獲取更快的速度,而如果不對齊,可能你需要的資料需要更多的存取次數才能得到。

       為了滿足這個條件,對於一個二維陣列來說(行優先row major),就希望每一行的開頭都滿足“對齊”。如果一行的長度不規整,導致下一行開頭不在指定的位置,就需要在每一行末尾進行填充(padding),從而使得每一行都對齊,這和BMP格式的畫素儲存是一個道理。

       pitch就是指每一行的位元組數 + padding的位元組數 。

       使用 cudaMemcpy2D 的目的僅是為了利用 pitch 機制提升二維陣列中元素的訪問速度。事實上二維陣列傳遞進視訊記憶體後,還是得按照一維陣列的規範去處理該二維矩陣。global 函式中目前不支援多下標訪問,好坑。。

程式碼示例

 1 #include "cuda.h"
 2 #include "cuda_runtime.h"
 3 
 4 #include <iostream>
 5 
 6 using namespace std;
 7 
 8 // 定義測試二維陣列的行列數
 9 const int R = 5;
10 const int C = 10;
11 
12 int main()
13 {
14     // 定義一個用於測試的二維陣列,其每個元素都賦值為0,並將其列印出來。
15     int array2D[R][C];
16     cout << "傳輸前的測試矩陣:" << endl;
17     for (int i=0; i<R; i++) {
18         for (int j=0; j<C; j++) {
19             array2D[i][j] = 0;
20             cout << array2D[i][j] << " ";
21         }
22         cout << endl;
23     }
24 
25     // 再定義另一個同樣大小的二維陣列用於獲取從視訊記憶體傳回的結果
26     int result[R][C];
27     cout << "傳輸前的結果矩陣:" << endl;
28     for (int i=0; i<R; i++) {
29         for (int j=0; j<C; j++) {
30             result[i][j] = 1;
31             cout << result[i][j] << " ";
32         }
33         cout << endl;
34     }
35 
36     // 為此二維陣列在視訊記憶體中分配記憶體
37     int *d_array2D;
38     cudaMalloc ((void**)&d_array2D, sizeof(int)*R*C);
39 
40     // 獲取視訊記憶體中的二維陣列的 pitch 值
41     size_t d_pitch;
42     cudaMallocPitch ((void**) &d_array2D, &d_pitch, sizeof(int)*C, R);
43 
44     // 將二維陣列轉移進視訊記憶體
45      cudaMemcpy2D (
46          d_array2D,    // 目的地址
47          d_pitch,    // 目的 pitch
48          array2D,    // 源地址
49          sizeof(int)*C,    // 源 pitch
50          sizeof(int)*C,    // 資料拷貝寬度
51          R,    // 資料拷貝高度
52          cudaMemcpyHostToDevice    // 資料傳遞方向
53         );
54 
55      // 將二維陣列從視訊記憶體傳輸回主機端的結果矩陣中
56      cudaMemcpy2D (
57          result,    // 目的地址
58          sizeof(int)*C,    // 目的 pitch
59          d_array2D,    // 源地址
60          d_pitch,    // 源 pitch
61          sizeof(int)*C,    // 資料拷貝寬度
62          R,    // 資料拷貝高度
63          cudaMemcpyDeviceToHost    // 資料傳遞方向
64         );
65 
66     // 列印傳回到結果矩陣的資料 
67     cout << "從視訊記憶體獲取到測試矩陣後的結果矩陣:" << endl;
68     for (int i=0; i<R; i++) {
69         for (int j=0; j<C; j++) {
70             cout << result[i][j] << " ";
71         }
72         cout << endl;
73     }
74 
75     cudaFree (d_array2D);
76 
77     cin.get();
78 
79     return EXIT_SUCCESS;
80 }

執行測試

      

小結

       本文介紹的僅僅是二維陣列在兩端之間的傳輸!當二維陣列傳遞進了視訊記憶體,在對其操作的過程中,是需要對其進行一個一維到二維的下標操作轉換的,global 中不支援多下標訪問。之所以加入 pitch 並使用 cudaMemcpy2D 只是為了提高元素的訪問速度。

       如果需要具體處理傳遞進入的二維陣列,還要將 pitch 也作為引數傳遞進 kernel 函式,如下所示:

 1 // 下面的 kernel 函式將二維陣列的所有位置為 2
 2 __global__
 3 void kernelFun (int *d_array2D, int pitch)
 4 {
 5     for (int i=0; i<R; i++) {
 6         int *row = (int *)((char *)d_array2D+i*pitch);
 7         for (int j=0; j<C; j++) {
 8             row[j] = 2;
 9         }
10     }
11 
12     return;
13 }

 

相關文章