CUDA一維紋理記憶體

獨孤九劍-風清揚發表於2020-10-27

紋理一詞來源於GPU圖形世界,GPU通用平行計算“盜用”了紋理一詞,定義了一個紋理記憶體的概念。紋理記憶體快取在 裝置上,在某些情況下能減少對記憶體的請求並降低記憶體頻寬的使用,是專門為那些在記憶體訪問模式中存在大量空間區域性性的圖形應用而設計,意味著一個執行緒讀取的位置可能與鄰近執行緒讀取的位置“非常接近”。對於GPU核心而言,紋理記憶體是隻讀記憶體,並且只有通過特殊的紋理API才能對其訪問


紋理記憶體分為一維紋理記憶體和二維紋理記憶體,理解紋理記憶體最好的方式是丟掉“紋理”兩個字,紋理記憶體本質上是一塊記憶體,是GPU在特定應用中對一維、二維變數的特殊宣告定義以及特殊使用,這種特殊使用能夠減少記憶體流量,提升運算效能。

紋理變數(引用)必須宣告為檔案作用域內的全域性變數,這裡先探討一下一維紋理記憶體的使用方法。一維紋理記憶體的關鍵操作如下:


  • 1、用texture<型別>型別宣告。

如宣告一個unsigned char 型的一維紋理tex1,格式為:

texture<unsigned char,1,cudaReadmodeElementType> tex1;


  • 2、通過cudaBindTexture()繫結到紋理記憶體中,並關聯到對應的資料上。

如將unsigned char型別的dev_A繫結到一維紋理tex1上,格式為:

cudaBindTexture(0,tex1,dev_A);

注意一旦將資料繫結到一個紋理記憶體上,該資料就已經傳輸到了裝置快取上,在核函式中就可以直接訪問,不再需要額外傳入。


  • 3、 通過tex1Dfetch()來讀取紋理記憶體中的資料。

紋理記憶體是一種特殊的記憶體,需要使用特定的紋理API來訪問其中的資料。如訪問tex1陣列的第3個元素,格式為:

tex1Dfetch(tex1,2);


  • 4、 通過cudaUnbindTexture()取消繫結紋理記憶體。

紋理記憶體使用完之後需要取消繫結,釋放空間,如解除紋理tex1的繫結,格式為:

cudaUnbindTexture(tex1);


考慮一個簡單的應用,把一個長度是100的向量A中的資料拷貝到一個向量B中,使用普通CPU程式設計實現如下:

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. #define _length 100  
  6.   
  7. //CPU函式實現複製一個陣列  
  8. void Copy_CPU(unsigned int * listSource, unsigned int * listTarget, int length)  
  9. {  
  10.     for (int i = 0; i < length; i++)  
  11.     {  
  12.         listTarget[i] = listSource[i];  
  13.     }  
  14. }  
  15.   
  16. int main()  
  17. {  
  18.     unsigned int * listSource = new unsigned int[_length];  
  19.     unsigned int * listTarget = new unsigned int[_length];  
  20.   
  21.     //賦值  
  22.     for (int i = 0; i < _length; i++)  
  23.     {  
  24.         listSource[i] = i;  
  25.     }  
  26.   
  27.     //呼叫CPU複製函式  
  28.     Copy_CPU(listSource, listTarget, _length);  
  29.   
  30.     cout << "原始資料: ";  
  31.     for (int i = 0; i < _length; i++)  
  32.     {  
  33.         cout << listSource[i] << " ";  
  34.     }  
  35.     cout << endl << endl << "通過CPU拷貝的資料: ";  
  36.     for (int i = 0; i < _length; i++)  
  37.     {  
  38.         cout << listTarget[i] << " ";  
  39.     }  
  40.     getchar();  
  41. }  

  
  1. #include <iostream>
  2. using namespace std;
  3. #define _length 100
  4. //CPU函式實現複製一個陣列
  5. void Copy_CPU(unsigned int * listSource, unsigned int * listTarget, int length)
  6. {
  7. for ( int i = 0; i < length; i++)
  8. {
  9. listTarget[i] = listSource[i];
  10. }
  11. }
  12. int main()
  13. {
  14. unsigned int * listSource = new unsigned int[_length];
  15. unsigned int * listTarget = new unsigned int[_length];
  16. //賦值
  17. for ( int i = 0; i < _length; i++)
  18. {
  19. listSource[i] = i;
  20. }
  21. //呼叫CPU複製函式
  22. Copy_CPU(listSource, listTarget, _length);
  23. cout << "原始資料: ";
  24. for ( int i = 0; i < _length; i++)
  25. {
  26. cout << listSource[i] << " ";
  27. }
  28. cout << endl << endl << "通過CPU拷貝的資料: ";
  29. for ( int i = 0; i < _length; i++)
  30. {
  31. cout << listTarget[i] << " ";
  32. }
  33. getchar();
  34. }

執行結果:



使用GPU程式設計,普通變數程式設計實現

  1. #include"cuda_runtime.h"  
  2. #include"device_launch_parameters.h"  
  3. #include<iostream>  
  4.   
  5. #define _length 100  
  6.   
  7. using namespace std;  
  8.   
  9. //宣告要呼叫的Copy_GPU函式  
  10. extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length);  
  11.   
  12. void main(int argc, char** argv)  
  13. {  
  14.     unsigned int *listSource = new unsigned int[_length];  
  15.     unsigned int *listTarget = new unsigned int[_length];  
  16.   
  17.     //賦值  
  18.     for (int i = 0; i < _length; i++)  
  19.     {  
  20.         listSource[i] = i;  
  21.     }  
  22.   
  23.     // 呼叫Copy_GPU函式,Copy_GPU中會呼叫gpu端的kernel函式  
  24.     Copy_GPU(listSource, listTarget, _length);  
  25.   
  26.     cout << "原始資料: ";  
  27.     for (int i = 0; i < _length; i++)  
  28.     {  
  29.         cout << listSource[i] << " ";  
  30.     }  
  31.     cout << endl << endl << "通過GPU普通記憶體拷貝的資料: ";  
  32.     for (int i = 0; i < _length; i++)  
  33.     {  
  34.         cout << listTarget[i] << " ";  
  35.     }  
  36.     getchar();  
  37. }  
  38.   
  39. //核心程式碼,在gpu端執行的kernel,  
  40. __global__ void Blending_Texture(unsigned int* listSource, unsigned int* listTarget, int size)  
  41. {  
  42.     //通過執行緒ID得到陣列下標  
  43.     int index = blockIdx.x * blockDim.x + threadIdx.x;  
  44.   
  45.     if (index < size)  
  46.         listTarget[index] = listSource[index];  
  47. }  
  48.   
  49. void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)  
  50. {  
  51.     int data_size = length * sizeof(unsigned int);  
  52.   
  53.     unsigned int *dev_Source;  
  54.     unsigned int *dev_Target;  
  55.   
  56.     //在裝置上申請視訊記憶體空間  
  57.     cudaMalloc((void**)&dev_Source, data_size);  
  58.     cudaMalloc((void**)&dev_Target, data_size);  
  59.   
  60.     //將host端的資料拷貝到device端  
  61.     cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice);  
  62.   
  63.     //呼叫kernel  
  64.     Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Source, dev_Target, _length);  
  65.   
  66.     //將結果拷貝到host端 ☆host就是CPU  
  67.     cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost);  
  68.   
  69.     //釋放記憶體空間  
  70.     cudaFree(dev_Source);  
  71.     cudaFree(dev_Target);  
  72. }  

  
  1. #include"cuda_runtime.h"
  2. #include"device_launch_parameters.h"
  3. #include<iostream>
  4. #define _length 100
  5. using namespace std;
  6. //宣告要呼叫的Copy_GPU函式
  7. extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length);
  8. void main(int argc, char** argv)
  9. {
  10. unsigned int *listSource = new unsigned int[_length];
  11. unsigned int *listTarget = new unsigned int[_length];
  12. //賦值
  13. for ( int i = 0; i < _length; i++)
  14. {
  15. listSource[i] = i;
  16. }
  17. // 呼叫Copy_GPU函式,Copy_GPU中會呼叫gpu端的kernel函式
  18. Copy_GPU(listSource, listTarget, _length);
  19. cout << "原始資料: ";
  20. for ( int i = 0; i < _length; i++)
  21. {
  22. cout << listSource[i] << " ";
  23. }
  24. cout << endl << endl << "通過GPU普通記憶體拷貝的資料: ";
  25. for ( int i = 0; i < _length; i++)
  26. {
  27. cout << listTarget[i] << " ";
  28. }
  29. getchar();
  30. }
  31. //核心程式碼,在gpu端執行的kernel,
  32. __global__ void Blending_Texture(unsigned int* listSource, unsigned int* listTarget, int size)
  33. {
  34. //通過執行緒ID得到陣列下標
  35. int index = blockIdx.x * blockDim.x + threadIdx.x;
  36. if (index < size)
  37. listTarget[index] = listSource[index];
  38. }
  39. void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)
  40. {
  41. int data_size = length * sizeof( unsigned int);
  42. unsigned int *dev_Source;
  43. unsigned int *dev_Target;
  44. //在裝置上申請視訊記憶體空間
  45. cudaMalloc(( void**)&dev_Source, data_size);
  46. cudaMalloc(( void**)&dev_Target, data_size);
  47. //將host端的資料拷貝到device端
  48. cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice);
  49. //呼叫kernel
  50. Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Source, dev_Target, _length);
  51. //將結果拷貝到host端 ☆host就是CPU
  52. cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost);
  53. //釋放記憶體空間
  54. cudaFree(dev_Source);
  55. cudaFree(dev_Target);
  56. }


執行結果:




使用GPU程式設計,一維紋理變數程式設計實現

  1. #include"cuda_runtime.h"  
  2. #include"device_launch_parameters.h"  
  3. #include<iostream>  
  4.   
  5. #define _length 100  
  6.   
  7. using namespace std;  
  8.   
  9. //宣告紋理,用來繫結紋理,其實也就是個紋理標識  
  10. texture<unsigned int, 1, cudaReadModeElementType> rT1;  
  11.   
  12. //宣告要呼叫的Copy_GPU函式  
  13. extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length);  
  14.   
  15. void main(int argc, char** argv)  
  16. {  
  17.     unsigned int *listSource = new unsigned int[_length];  
  18.     unsigned int *listTarget = new unsigned int[_length];  
  19.   
  20.     //賦值  
  21.     for (int i = 0; i < _length; i++)  
  22.     {  
  23.         listSource[i] = i;  
  24.     }  
  25.   
  26.     // 呼叫Copy_GPU函式,Copy_GPU中會呼叫gpu端的kernel函式  
  27.     Copy_GPU(listSource, listTarget, _length);  
  28.   
  29.     cout << "原始資料: ";  
  30.     for (int i = 0; i < _length; i++)  
  31.     {  
  32.         cout << listSource[i] << " ";  
  33.     }  
  34.     cout << endl << endl << "通過GPU紋理記憶體拷貝的資料: ";  
  35.     for (int i = 0; i < _length; i++)  
  36.     {  
  37.         cout << listTarget[i] << " ";  
  38.     }  
  39.     getchar();  
  40. }  
  41.   
  42. //核心程式碼,在gpu端執行的kernel,  
  43. __global__ void Blending_Texture(unsigned int* listTarget, int size)  
  44. {  
  45.     //通過執行緒ID得到陣列下標  
  46.     int index = blockIdx.x * blockDim.x + threadIdx.x;  
  47.   
  48.     //通過紋理獲取函式得到資料再運算  
  49.     if (index < size)  
  50.         listTarget[index] = tex1Dfetch(rT1, index);  
  51. }  
  52.   
  53. void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)  
  54. {  
  55.     int data_size = length * sizeof(unsigned int);  
  56.   
  57.     unsigned int *dev_Source;  
  58.     unsigned int *dev_Target;  
  59.   
  60.     //在裝置上申請視訊記憶體空間  
  61.     cudaMalloc((void**)&dev_Source, data_size);  
  62.     cudaMalloc((void**)&dev_Target, data_size);  
  63.   
  64.     //將host端的資料拷貝到device端  
  65.     cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice);  
  66.   
  67.     //繫結紋理,繫結的紋理標識對應的資料   
  68.     cudaBindTexture(0, rT1, dev_Source);  
  69.   
  70.     //呼叫kernel  
  71.     Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Target, _length);  
  72.   
  73.     //將結果拷貝到host端 ☆host就是CPU  
  74.     cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost);  
  75.   
  76.     //取消繫結  
  77.     cudaUnbindTexture(rT1);  
  78.   
  79.     //釋放記憶體空間  
  80.     cudaFree(dev_Source);  
  81.     cudaFree(dev_Target);  
  82. }  

  
  1. #include"cuda_runtime.h"
  2. #include"device_launch_parameters.h"
  3. #include<iostream>
  4. #define _length 100
  5. using namespace std;
  6. //宣告紋理,用來繫結紋理,其實也就是個紋理標識
  7. texture< unsigned int, 1, cudaReadModeElementType> rT1;
  8. //宣告要呼叫的Copy_GPU函式
  9. extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length);
  10. void main(int argc, char** argv)
  11. {
  12. unsigned int *listSource = new unsigned int[_length];
  13. unsigned int *listTarget = new unsigned int[_length];
  14. //賦值
  15. for ( int i = 0; i < _length; i++)
  16. {
  17. listSource[i] = i;
  18. }
  19. // 呼叫Copy_GPU函式,Copy_GPU中會呼叫gpu端的kernel函式
  20. Copy_GPU(listSource, listTarget, _length);
  21. cout << "原始資料: ";
  22. for ( int i = 0; i < _length; i++)
  23. {
  24. cout << listSource[i] << " ";
  25. }
  26. cout << endl << endl << "通過GPU紋理記憶體拷貝的資料: ";
  27. for ( int i = 0; i < _length; i++)
  28. {
  29. cout << listTarget[i] << " ";
  30. }
  31. getchar();
  32. }
  33. //核心程式碼,在gpu端執行的kernel,
  34. __global__ void Blending_Texture(unsigned int* listTarget, int size)
  35. {
  36. //通過執行緒ID得到陣列下標
  37. int index = blockIdx.x * blockDim.x + threadIdx.x;
  38. //通過紋理獲取函式得到資料再運算
  39. if (index < size)
  40. listTarget[index] = tex1Dfetch(rT1, index);
  41. }
  42. void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)
  43. {
  44. int data_size = length * sizeof( unsigned int);
  45. unsigned int *dev_Source;
  46. unsigned int *dev_Target;
  47. //在裝置上申請視訊記憶體空間
  48. cudaMalloc(( void**)&dev_Source, data_size);
  49. cudaMalloc(( void**)&dev_Target, data_size);
  50. //將host端的資料拷貝到device端
  51. cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice);
  52. //繫結紋理,繫結的紋理標識對應的資料
  53. cudaBindTexture( 0, rT1, dev_Source);
  54. //呼叫kernel
  55. Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Target, _length);
  56. //將結果拷貝到host端 ☆host就是CPU
  57. cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost);
  58. //取消繫結
  59. cudaUnbindTexture(rT1);
  60. //釋放記憶體空間
  61. cudaFree(dev_Source);
  62. cudaFree(dev_Target);
  63. }

執行結果:




再舉一個使用CUDA+OpenCv程式設計,實現複製一幅影像的例子:

  1. #include"cuda_runtime.h"  
  2. #include<iostream>  
  3. #include<highgui/highgui.hpp>  
  4. #include<imgproc/imgproc.hpp>  
  5.   
  6. #define DIM 512   //影像尺寸  
  7.   
  8. using namespace std;  
  9. using namespace cv;  
  10.   
  11. //一維紋理宣告  
  12. texture<unsigned char, 1, cudaReadModeElementType> rT1;  
  13.   
  14. __global__ void Kernel_Copy(unsigned char* imageTarget)  
  15. {  
  16.     int x = threadIdx.x + blockIdx.x*blockDim.x;  
  17.     int y = threadIdx.y + blockIdx.y*blockDim.y;  
  18.     int offset = x + y*blockDim.x*gridDim.x;  
  19.   
  20.     //複製影像  
  21.     imageTarget[offset * 3 + 2] = tex1Dfetch(rT1, offset * 3 + 2);  
  22.     imageTarget[offset * 3 + 1] = tex1Dfetch(rT1, offset * 3 + 1);  
  23.     imageTarget[offset * 3 + 0] = tex1Dfetch(rT1, offset * 3 + 0);  
  24. }  
  25.   
  26. void main(int argc, char** argv)  
  27. {  
  28.     Mat image = imread("D:\\lena.jpg");  
  29.     Mat imageSource;  
  30.     resize(image, imageSource, Size(DIM, DIM)); //調整影像大小  
  31.   
  32.     Mat imageTarget = Mat(Size(DIM, DIM), CV_8UC3, Scalar::all(0));  
  33.   
  34.     //分配空間  
  35.     unsigned char *dev_imageSource;  
  36.     unsigned char *dev_imageTarget;  
  37.     cudaMalloc((void**)&dev_imageSource, 3 * imageSource.rows*imageSource.cols);  
  38.     cudaMalloc((void**)&dev_imageTarget, 3 * imageSource.rows*imageSource.cols);  
  39.   
  40.     cudaMemcpy(dev_imageSource, imageSource.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice);  
  41.     cudaMemcpy(dev_imageTarget, imageTarget.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice);  
  42.   
  43.     //繫結紋理  
  44.     cudaBindTexture(0, rT1, dev_imageSource);  
  45.   
  46.     dim3 grids(DIM / 16, DIM / 16);  
  47.     dim3 threads(16, 16);  
  48.     //呼叫kernel  
  49.     Kernel_Copy << < grids, threads >> > (dev_imageTarget);  
  50.   
  51.     //將結果拷貝到host端 ☆host就是CPU  
  52.     cudaMemcpy(imageTarget.data, dev_imageTarget, 3 * imageSource.cols*imageSource.rows, cudaMemcpyDeviceToHost);  
  53.   
  54.     imshow("CUDA紋理記憶體使用示例", imageTarget);  
  55.     waitKey();  
  56.   
  57.     //解除紋理繫結  
  58.     cudaUnbindTexture(rT1);  
  59.   
  60.     cudaFree(dev_imageSource);  
  61.     cudaFree(dev_imageSource);  
  62. }  

  
  1. #include"cuda_runtime.h"
  2. #include<iostream>
  3. #include<highgui/highgui.hpp>
  4. #include<imgproc/imgproc.hpp>
  5. #define DIM 512 //影像尺寸
  6. using namespace std;
  7. using namespace cv;
  8. //一維紋理宣告
  9. texture< unsigned char, 1, cudaReadModeElementType> rT1;
  10. __global__ void Kernel_Copy(unsigned char* imageTarget)
  11. {
  12. int x = threadIdx.x + blockIdx.x*blockDim.x;
  13. int y = threadIdx.y + blockIdx.y*blockDim.y;
  14. int offset = x + y*blockDim.x*gridDim.x;
  15. //複製影像
  16. imageTarget[offset * 3 + 2] = tex1Dfetch(rT1, offset * 3 + 2);
  17. imageTarget[offset * 3 + 1] = tex1Dfetch(rT1, offset * 3 + 1);
  18. imageTarget[offset * 3 + 0] = tex1Dfetch(rT1, offset * 3 + 0);
  19. }
  20. void main(int argc, char** argv)
  21. {
  22. Mat image = imread( "D:\\lena.jpg");
  23. Mat imageSource;
  24. resize(image, imageSource, Size(DIM, DIM)); //調整影像大小
  25. Mat imageTarget = Mat(Size(DIM, DIM), CV_8UC3, Scalar::all( 0));
  26. //分配空間
  27. unsigned char *dev_imageSource;
  28. unsigned char *dev_imageTarget;
  29. cudaMalloc(( void**)&dev_imageSource, 3 * imageSource.rows*imageSource.cols);
  30. cudaMalloc(( void**)&dev_imageTarget, 3 * imageSource.rows*imageSource.cols);
  31. cudaMemcpy(dev_imageSource, imageSource.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice);
  32. cudaMemcpy(dev_imageTarget, imageTarget.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice);
  33. //繫結紋理
  34. cudaBindTexture( 0, rT1, dev_imageSource);
  35. dim3 grids(DIM / 16, DIM / 16);
  36. dim3 threads(16, 16);
  37. //呼叫kernel
  38. Kernel_Copy << < grids, threads >> > (dev_imageTarget);
  39. //將結果拷貝到host端 ☆host就是CPU
  40. cudaMemcpy(imageTarget.data, dev_imageTarget, 3 * imageSource.cols*imageSource.rows, cudaMemcpyDeviceToHost);
  41. imshow( "CUDA紋理記憶體使用示例", imageTarget);
  42. waitKey();
  43. //解除紋理繫結
  44. cudaUnbindTexture(rT1);
  45. cudaFree(dev_imageSource);
  46. cudaFree(dev_imageSource);
  47. }

執行結果:



相關文章