CUDA C Programming Guide 在線教程學習筆記 Part 3


▶ 表面內存使用

● 創建 cuda 數組時使用標志 cudaArraySurfaceLoadStore 來創建表面內存,可以用表面對象(surface object)或表面引用(surface reference)來對其進行讀寫。

● 使用 Surface Object API

■ 涉及的結構定義、接口函數。

1 // vector_types.h
2 struct __device_builtin__ __align__(4) uchar4
3 {
4     unsigned char x, y, z, w;
5 };
6 
7 // surface_types.h
8 typedef __device_builtin__ unsigned long long cudaSurfaceObject_t;

■ 完整的測試代碼,使用表面內存進行簡單的讀寫。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 #include <cuda_runtime_api.h>
 5 #include "device_launch_parameters.h"
 6 
 7 #define CEIL(x,y) (((x) + (y) - 1) / (y) + 1)
 8 
 9 __global__ void myKernel(cudaSurfaceObject_t inputSurfObj, cudaSurfaceObject_t outputSurfObj, int width, int height)
10 {
11     unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;
12     unsigned int idy = blockIdx.y * blockDim.y + threadIdx.y;
13     if (idx < width && idy < height)
14     {
15         uchar4 data;
16         // 簡單的表面內存讀寫,使用了字節地址,而不是簡單的線程編號
17         surf2Dread(&data, inputSurfObj, sizeof(float) * idx, idy);
18         surf2Dwrite(data, outputSurfObj, sizeof(float) * idx, idy);
19     }
20     cudaBindSurfaceToArray();
21 }
22 
23 int main()
24 {
25     // 基本數據
26     int i;
27     float *h_data, *d_data; 
28     int width = 32;
29     int height = 32;
30 
31     int size = sizeof(float)*width*height;
32     h_data = (float *)malloc(size);
33     cudaMalloc((void **)&d_data, size);
34 
35     for (i = 0; i < width*height; i++)
36         h_data[i] = (float)i;
37 
38     printf("\n\n");
39     for (i = 0; i < width*height; i++)
40     {
41         printf("%6.1f ", h_data[i]);
42         if ((i + 1) % width == 0)
43             printf("\n");
44     }
45     
46     // 申請 cuda 數組
47     cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindUnsigned);
48     cudaArray* cuInputArray;
49     cudaMallocArray(&cuInputArray, &channelDesc, width, height, cudaArraySurfaceLoadStore); 
50     cudaArray* cuOutputArray;
51     cudaMallocArray(&cuOutputArray, &channelDesc, width, height, cudaArraySurfaceLoadStore);
52     cudaMemcpyToArray(cuInputArray, 0, 0, h_data, size,cudaMemcpyHostToDevice);
53 
54     // 指定表面內存
55     struct cudaResourceDesc resDesc;
56     memset(&resDesc, 0, sizeof(resDesc));
57     resDesc.resType = cudaResourceTypeArray;
58 
59     // 創建表面對象
60     resDesc.res.array.array = cuInputArray;
61     cudaSurfaceObject_t inputSurfObj = 0;
62     cudaCreateSurfaceObject(&inputSurfObj, &resDesc);
63     resDesc.res.array.array = cuOutputArray;
64     cudaSurfaceObject_t outputSurfObj = 0;
65     cudaCreateSurfaceObject(&outputSurfObj, &resDesc);
66 
67     // 運行核函數
68     dim3 dimBlock(16, 16);
69     dim3 dimGrid(CEIL(width, dimBlock.x), CEIL(height, dimBlock.y));
70     myKernel << <dimGrid, dimBlock >> > (inputSurfObj, outputSurfObj, width, height);
71 
72     // 結果回收和檢查結果
73     memset(h_data,0,size);// 刷掉原來的 h_data,再用 cuOutputArray 的數據寫入     
74     cudaMemcpyFromArray(h_data, cuOutputArray, 0, 0, size, cudaMemcpyDeviceToHost);
75 
76     printf("\n\n");
77     for (i = 0; i < width*height; i++)
78     {
79         printf("%6.1f ", h_data[i]);
80         if ((i + 1) % width == 0)
81             printf("\n");
82     }
83 
84     // 回收工作
85     cudaDestroySurfaceObject(inputSurfObj);
86     cudaDestroySurfaceObject(outputSurfObj);
87     cudaFreeArray(cuInputArray);
88     cudaFreeArray(cuOutputArray);
89 
90     getchar();
91     return 0;
92 }

 

● 使用 Surface Reference API。

■ 表面引用的一些只讀屬性需要在聲明的時候指定,以便編譯時提前確定,只能在全局作用域內靜態指定,不能作為參數傳遞給函數。使用 surface 指定紋理引用屬性,Datatype 為數據類型,Type 為紋理引用類型,有 7 種,默認 cudaSurfaceType1D。

 1 surface<void, Type> surfRef;
 2 
 3 // cuda_texture_types.h
 4 template<class T, int dim = 1>
 5 struct __device_builtin_surface_type__ surface : public surfaceReference
 6 {
 7 #if !defined(__CUDACC_RTC__)
 8     __host__ surface(void)
 9     {
10         channelDesc = cudaCreateChannelDesc<T>();
11     }
12 
13     __host__ surface(struct cudaChannelFormatDesc desc)
14     {
15         channelDesc = desc;
16     }
17 #endif /* !__CUDACC_RTC__ */  
18 };
19 
20 //surface_types.h
21 #define cudaSurfaceType1D              0x01
22 #define cudaSurfaceType2D              0x02
23 #define cudaSurfaceType3D              0x03
24 #define cudaSurfaceTypeCubemap         0x0C
25 #define cudaSurfaceType1DLayered       0xF1
26 #define cudaSurfaceType2DLayered       0xF2
27 #define cudaSurfaceTypeCubemapLayered  0xFC
28 
29 // 訪問邊界模式
30 enum __device_builtin__ cudaSurfaceBoundaryMode
31 {
32     cudaBoundaryModeZero = 0,    // 0 邊界模式
33     cudaBoundaryModeClamp = 1,   // 擠壓模式
34     cudaBoundaryModeTrap = 2     // 陷阱模式
35 };
36 
37 // ?表面格式模式
38 enum __device_builtin__  cudaSurfaceFormatMode
39 {
40     cudaFormatModeForced = 0,   // 強制模式
41     cudaFormatModeAuto = 1      // 自動模式
42 };
43 
44 // 表面引用的通道描述
45 struct __device_builtin__ surfaceReference
46 {
47     struct cudaChannelFormatDesc channelDesc;
48 };
49 
50 // cuda_runtime_api.h
51 extern __host__ cudaError_t CUDARTAPI cudaBindSurfaceToArray(const struct surfaceReference *surfref, cudaArray_const_t array, const struct cudaChannelFormatDesc *desc);

 

■ 表面引用使用字節地址來定位訪問(而不是像紋理那樣使用 fetch 函數),如以上代碼中 surf1Dread(surfRef, sizeof(float) * idx) 或是 surf1Dread(surfRef, sizeof(float) * idx) 。

■ 表面引用必須用函數 cudaBindSurfaceToArray() 綁定到 cuda 數組上才能使用,要求表面引用的維度、數據類型與該數組匹配,否則操作時未定義的,使用完后不需要特殊函數來解除綁定。

■ 將表面引用綁定到 cuda 數組上的范例代碼。

 1 // 准備工作
 2 surface<void, Type>surfRef;
 3 
 4 ...
 5 
 6 int width, height;
 7 size_t pitch;
 8 float *d_data;
 9 cudaMallocPitch((void **)&d_data, &pitch, sizeof(float)*width, height);
10 
11 // 第一種方法,低層 API
12 surfaceReference* surfRefPtr;
13 cudaGetSurfaceReference(&surfRefPtr, "surfRef");
14 cudaChannelFormatDesc channelDesc;
15 cudaGetChannelDesc(&channelDesc, cuArray);
16 cudaBindSurfaceToArray(surfRef, cuArray, &channelDesc);
17 
18 // 第二種方法,高層 API
19 cudaBindSurfaceToArray(surfRef, cuArray);

 

■ 完整的應用樣例代碼。與前面表面對象代碼的功能相同。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 #include <cuda_runtime_api.h>
 5 #include "device_launch_parameters.h"
 6 
 7 #define CEIL(x,y) (((x) + (y) - 1) / (y) + 1)
 8 
 9 // 聲明表面引用
10 surface<void, 2> inputSurfRef;
11 surface<void, 2> outputSurfRef;
12 
13 __global__ void myKernel(int width, int height)
14 {
15     unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;
16     unsigned int idy = blockIdx.y * blockDim.y + threadIdx.y;
17     if (idx < width && idy < height)
18     {
19         uchar4 data;
20         // 簡單的表面內存讀寫,使用了字節地址,而不是簡單的線程編號
21         surf2Dread(&data, inputSurfRef, sizeof(float) * idx, idy);
22         surf2Dwrite(data, outputSurfRef, sizeof(float) * idx, idy);
23     }
24 }
25 
26 int main()
27 {
28     // 基本數據
29     int i;
30     float *h_data, *d_data; 
31     int width = 32;
32     int height = 32;
33 
34     int size = sizeof(float)*width*height;
35     h_data = (float *)malloc(size);
36     cudaMalloc((void **)&d_data, size);
37 
38     for (i = 0; i < width*height; i++)
39         h_data[i] = (float)i;
40 
41     printf("\n\n");
42     for (i = 0; i < width*height; i++)
43     {
44         printf("%6.1f ", h_data[i]);
45         if ((i + 1) % width == 0)
46             printf("\n");
47     }
48     
49     // 申請 cuda 數組
50     cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindUnsigned);
51     cudaArray* cuInputArray;
52     cudaMallocArray(&cuInputArray, &channelDesc, width, height, cudaArraySurfaceLoadStore); 
53     cudaArray* cuOutputArray;
54     cudaMallocArray(&cuOutputArray, &channelDesc, width, height, cudaArraySurfaceLoadStore);
55     cudaMemcpyToArray(cuInputArray, 0, 0, h_data, size,cudaMemcpyHostToDevice);
56 
57     // 綁定表面引用,注意與表面對象的使用不一樣
58     cudaBindSurfaceToArray(inputSurfRef, cuInputArray);
59     cudaBindSurfaceToArray(outputSurfRef, cuOutputArray);
60 
61     // 運行核函數
62     dim3 dimBlock(16, 16);
63     dim3 dimGrid(CEIL(width, dimBlock.x), CEIL(height, dimBlock.y));
64     myKernel << <dimGrid, dimBlock >> > (width, height);
65 
66     // 結果回收和檢查結果
67     memset(h_data,0,size);// 刷掉原來的 h_data,再用 cuOutputArray 的數據寫入     
68     cudaMemcpyFromArray(h_data, cuOutputArray, 0, 0, size, cudaMemcpyDeviceToHost);
69 
70     printf("\n\n");
71     for (i = 0; i < width*height; i++)
72     {
73         printf("%6.1f ", h_data[i]);
74         if ((i + 1) % width == 0)
75             printf("\n");
76     }
77 
78     // 回收工作
79     cudaFreeArray(cuInputArray);
80     cudaFreeArray(cuOutputArray);
81 
82     getchar();
83     return 0;
84 }

 

▶ 立方體表面 Cubemap Surface 。 (想象成一個正方體的外表面)

● 一種特殊的二維分層表面。函數 surfCubemapread() 和函數 surfCubemapwrite() 來對其進行讀寫,使用一個整數下標和兩個浮點數有序組來定義層號和表面坐標。

 

▶ 分層立方體表面 Cubemap Layered Surfaces 。(想象成一個多層的正方體的各外表面)

● 一種特殊的二維分層表面。函數 surfCubemapread() 和函數 surfCubemapwrite() 來對齊進行讀寫。使用一個整數下標和兩個浮點數有序組來定義層號和表面坐標。

● 分層立方體貼圖紋理只能使用函數 cudaMAlloc3DArray() 加上 cudaArrayLayered 和 cudaArrayCubemap 標志來聲明,使用函數 texCubemapLayered() 來進行訪問濾波只在同一層內部進行,不會跨層執行。

 

▶ cuda 數組。

● cuda 優化的數組類型,可以有一維或二維或三維,每個元素可以有 1 個或 2 個或 4 個分量,各分量可以是 1 B 或 2 B 或 4 B 尺寸的有符號或無符號整數,或 2 B 或 4 B 尺寸的浮點數。cuda 數組只能用紋理訪問函數來訪問,或表面函數來進行讀寫。

● 紋理內存和表面內存都是可緩存的,且不能保證緩存和內存的一致性。同一個核函數中,用紋理訪問或表面訪問來讀取“已經全局寫入或表面寫入的內存”是未定義的。

 

▶ 壓縮版的 surface_types.h

 1 #if !defined(__SURFACE_TYPES_H__)
 2 #define __SURFACE_TYPES_H__
 3 
 4 #include "driver_types.h"
 5 
 6 #define cudaSurfaceType1D              0x01
 7 #define cudaSurfaceType2D              0x02
 8 #define cudaSurfaceType3D              0x03
 9 #define cudaSurfaceTypeCubemap         0x0C
10 #define cudaSurfaceType1DLayered       0xF1
11 #define cudaSurfaceType2DLayered       0xF2
12 #define cudaSurfaceTypeCubemapLayered  0xFC
13 
14 //CUDA Surface boundary modes
15 enum __device_builtin__ cudaSurfaceBoundaryMode
16 {
17     cudaBoundaryModeZero = 0,   // Zero boundary mode */
18     cudaBoundaryModeClamp = 1,  // Clamp boundary mode */
19     cudaBoundaryModeTrap = 2    // Trap boundary mode */
20 };
21 
22 //CUDA Surface format modes
23 enum __device_builtin__  cudaSurfaceFormatMode
24 {
25     cudaFormatModeForced = 0,   // Forced format mode */
26     cudaFormatModeAuto = 1      // Auto format mode */
27 };
28 
29 //CUDA Surface reference
30 struct __device_builtin__ surfaceReference
31 {
32     // Channel descriptor for surface reference
33     struct cudaChannelFormatDesc channelDesc;
34 };
35 
36 //An opaque value that represents a CUDA Surface object
37 typedef __device_builtin__ unsigned long long cudaSurfaceObject_t;
38 
39 
40 #endif

 


免責聲明!

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



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