爨爨爨好

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

矩阵乘法,使用一维线程块和共享内存。并且在静态代码和运行时编译两种条件下使用。

▶ 源代码:静态使用

  1 #include <stdio.h>
  2 #include <assert.h>
  3 #include <cuda_runtime.h>
  4 #include "device_launch_parameters.h"
  5 #include <helper_functions.h>
  6 #include <helper_cuda.h>
  7 
  8 template <int BLOCK_SIZE> __global__ void matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)
  9 {
 10     int bx = blockIdx.x;
 11     int by = blockIdx.y;
 12     int tx = threadIdx.x;
 13     int ty = threadIdx.y;
 14 
 15     int aBegin = wA * BLOCK_SIZE * by;  // A的行程起点
 16     int aEnd   = aBegin + wA - 1;       // A的行程终点
 17     int aStep  = BLOCK_SIZE;            // A的跨度(一个 block 为宽 BLOCK_SIZE 的一维条带,各线程分别对应其中的一个元素)
 18     int bBegin = BLOCK_SIZE * bx;       // B的行程起点
 19     int bStep  = BLOCK_SIZE * wB;       // B的跨度(一个 block 为高 BLOCK_SIZE 的一维条带,各线程分别对应其中的一个元素)
 20     float Csub = 0;
 21     
 22     for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep)
 23     {
 24         __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
 25         __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
 26         As[ty][tx] = A[a + wA * ty + tx];
 27         Bs[ty][tx] = B[b + wB * ty + tx];
 28         __syncthreads();
 29 
 30 #pragma unroll// 循环展开为 BLOCK_SIZE 个赋值语句,提高效率
 31         for (int k = 0; k < BLOCK_SIZE; ++k)
 32             Csub += As[ty][k] * Bs[k][tx];
 33         __syncthreads();
 34     }
 35 
 36     int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;
 37     C[c + wB * ty + tx] = Csub;
 38 }
 39 
 40 void constantInit(float *data, int size, float val)
 41 {
 42     for (int i = 0; i < size; ++i)
 43         data[i] = val;
 44 }
 45 
 46 int matrixMultiply(int argc, char **argv, int block_size, dim3 &dimsA, dim3 &dimsB)
 47 {
 48     unsigned int size_A = dimsA.x * dimsA.y;
 49     unsigned int mem_size_A = sizeof(float) * size_A;
 50     float *h_A = (float *)malloc(mem_size_A);
 51     unsigned int size_B = dimsB.x * dimsB.y;
 52     unsigned int mem_size_B = sizeof(float) * size_B;
 53     float *h_B = (float *)malloc(mem_size_B);
 54     constantInit(h_A, size_A, 1.0f);
 55     constantInit(h_B, size_B, 0.01f);
 56     dim3 dimsC(dimsB.x, dimsA.y, 1);
 57     unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);
 58     float *h_C = (float *) malloc(mem_size_C);
 59     float *d_A, *d_B, *d_C;
 60     cudaMalloc((void **) &d_A, mem_size_A);
 61     cudaMalloc((void **) &d_B, mem_size_B);
 62     cudaMalloc((void **) &d_C, mem_size_C);
 63     cudaMemcpy(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice);
 64     cudaMemcpy(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice);
 65 
 66     // 热身
 67     dim3 threads(block_size, block_size);
 68     dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);
 69     if (block_size == 16)
 70         matrixMulCUDA<16><<< grid, threads >>>(d_C, d_A, d_B, dimsA.x, dimsB.x);
 71     else
 72         matrixMulCUDA<32><<< grid, threads >>>(d_C, d_A, d_B, dimsA.x, dimsB.x);
 73     printf("done\n");
 74     cudaDeviceSynchronize();
 75 
 76     printf("Computing result using CUDA Kernel...\n");
 77     cudaEvent_t start;
 78     cudaEventCreate(&start);
 79     cudaEvent_t stop;
 80     cudaEventCreate(&stop);
 81     cudaEventRecord(start, NULL);
 82 
 83     int nIter = 300;
 84     for (int j = 0; j < nIter; j++)
 85     {
 86         if (block_size == 16)
 87             matrixMulCUDA<16><<< grid, threads >>>(d_C, d_A, d_B, dimsA.x, dimsB.x);
 88         else
 89             matrixMulCUDA<32><<< grid, threads >>>(d_C, d_A, d_B, dimsA.x, dimsB.x);
 90     }
 91     cudaEventRecord(stop, NULL);
 92     cudaEventSynchronize(stop);
 93 
 94     float msecTotal = 0.0f;
 95     cudaEventElapsedTime(&msecTotal, start, stop);
 96     float msecPerMatrixMul = msecTotal / nIter;
 97     double flopsPerMatrixMul = 2.0 * (double)dimsA.x * (double)dimsA.y * (double)dimsB.x;
 98     double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) / (msecPerMatrixMul / 1000.0f);
 99     printf("Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f Ops, WorkgroupSize= %u threads/block\n",
100         gigaFlops, msecPerMatrixMul, flopsPerMatrixMul, threads.x * threads.y);
101     cudaMemcpy(h_C, d_C, mem_size_C, cudaMemcpyDeviceToHost);
102     
103     // 检查结果,要求相对误差:|<x, y>_cpu - <x,y>_gpu| / <|x|, |y|>  < eps    
104     printf("Checking computed result for correctness: ");
105     bool correct = true;
106     double eps = 1.e-6 ; // machine zero
107     for (int i = 0; i < (int)(dimsC.x * dimsC.y); i++)
108     {
109         double abs_err = fabs(h_C[i] - (dimsA.x * valB));
110         double dot_length = dimsA.x;
111         double abs_val = fabs(h_C[i]);
112         double rel_err = abs_err/abs_val/dot_length ;
113         if (rel_err > eps)
114         {
115             printf("Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\n", i, h_C[i], dimsA.x*valB, eps);
116             correct = false;
117         }
118     }
119     printf("%s\n", correct ? "Result = PASS" : "Result = FAIL");
120 
121     free(h_A);
122     free(h_B);
123     free(h_C);
124     cudaFree(d_A);
125     cudaFree(d_B);
126     cudaFree(d_C);
127     printf("\nNOTE: The CUDA Samples are not meant for performance measurements. Results may vary when GPU Boost is enabled.\n");
128     if (correct)
129         return EXIT_SUCCESS;
130     else
131         return EXIT_FAILURE;
132 }
133 
134 int main(int argc, char **argv)
135 {
136     printf("[Matrix Multiply Using CUDA] - Starting...\n");
137 
138     if (checkCmdLineFlag(argc, (const char **)argv, "help") || checkCmdLineFlag(argc, (const char **)argv, "?"))
139     {
140         printf("Usage -device=n (n >= 0 for deviceID)\n");
141         printf("      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\n");
142         printf("      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\n");
143         printf("  Note: Outer matrix dimensions of A & B matrices must be equal.\n");
144         exit(EXIT_SUCCESS);
145     }
146 
147     int devID = 0;// 指定设备,默认用0号设备
148     if (checkCmdLineFlag(argc, (const char **)argv, "device"))
149     {
150         devID = getCmdLineArgumentInt(argc, (const char **)argv, "device");
151         cudaSetDevice(devID);
152     }
153     cudaDeviceProp deviceProp;
154     cudaGetDevice(&devID);
155     cudaGetDeviceProperties(&deviceProp, devID);
156 
157     if (deviceProp.computeMode == cudaComputeModeProhibited)
158     {
159         fprintf(stderr, "Error: device is running in <Compute Mode Prohibited>, no threads can use ::cudaSetDevice().\n");
160         exit(EXIT_SUCCESS);
161     }
162 
163     int block_size = (deviceProp.major < 2) ? 16 : 32;
164 
165     dim3 dimsA(5*2*block_size, 5*2*block_size, 1);
166     dim3 dimsB(5*4*block_size, 5*2*block_size, 1);
167 
168     // 使用命令行指定的A、B的维度参数
169     if (checkCmdLineFlag(argc, (const char **)argv, "wA"))
170         dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, "wA");
171     if (checkCmdLineFlag(argc, (const char **)argv, "hA"))
172         dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, "hA");
173     if (checkCmdLineFlag(argc, (const char **)argv, "wB"))
174         dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, "wB");
175     if (checkCmdLineFlag(argc, (const char **)argv, "hB"))
176         dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, "hB");
177     if (dimsA.x != dimsB.y)
178     {
179         printf("Error: outer matrix dimensions must be equal. (%d != %d)\n",
180                dimsA.x, dimsB.y);
181         exit(EXIT_FAILURE);
182     }
183     printf("MatrixA(%d,%d), MatrixB(%d,%d)\n", dimsA.x, dimsA.y, dimsB.x, dimsB.y);
184 
185     int matrix_result = matrixMultiply(argc, argv, block_size, dimsA, dimsB);
186 
187     getchar();
188     exit(matrix_result);
189 }

 

▶ 源代码:运行时编译

 1 /*matrixMul_kernel.cu*/
 2 template <int BLOCK_SIZE> __device__ void matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)
 3 {
 4     int bx = blockIdx.x;
 5     int by = blockIdx.y;
 6     int tx = threadIdx.x;
 7     int ty = threadIdx.y;
 8     int aBegin = wA * BLOCK_SIZE * by;
 9     int aEnd   = aBegin + wA - 1;
10     int aStep  = BLOCK_SIZE;
11     int bBegin = BLOCK_SIZE * bx;
12     int bStep = BLOCK_SIZE * wB;
13     float Csub = 0;
14     for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep)
15     {
16         __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
17         __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
18         As[ty][tx] = A[a + wA * ty + tx];
19         Bs[ty][tx] = B[b + wB * ty + tx];
20         __syncthreads();
21 #pragma unroll
22         for (int k = 0; k < BLOCK_SIZE; ++k)
23             Csub += As[ty][k] * Bs[k][tx];
24         __syncthreads();
25     }
26     int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;
27     C[c + wB * ty + tx] = Csub;
28 }
29 
30 extern "C" __global__ void  matrixMulCUDA_block16(float *C, float *A, float *B, int wA, int wB)
31 {
32     matrixMulCUDA<16>(C,A,B,wA,wB);
33 }
34 
35 extern "C" __global__ void  matrixMulCUDA_block32(float *C, float *A, float *B, int wA, int wB)
36 {
37     matrixMulCUDA<32>(C,A,B,wA,wB);
38 }
  1 /*matrixMul.cpp*/
  2 #include <stdio.h>
  3 #include <assert.h>
  4 #include <cuda_runtime.h>
  5 #include "device_launch_parameters.h"
  6 #include "nvrtc_helper.h"
  7 #include <helper_functions.h>
  8 
  9 void constantInit(float *data, int size, float val)
 10 {
 11     for (int i = 0; i < size; ++i)
 12         data[i] = val;
 13 }
 14 
 15 int matrixMultiply(int argc, char **argv, int block_size, dim3 &dimsA, dim3 &dimsB)
 16 {
 17     // Allocate host memory for matrices A and B
 18     unsigned int size_A = dimsA.x * dimsA.y;
 19     unsigned int mem_size_A = sizeof(float) * size_A;
 20     float *h_A = (float *)malloc(mem_size_A);
 21     unsigned int size_B = dimsB.x * dimsB.y;
 22     unsigned int mem_size_B = sizeof(float) * size_B;
 23     float *h_B = (float *)malloc(mem_size_B);
 24     const float valB = 0.01f;
 25     constantInit(h_A, size_A, 1.0f);
 26     constantInit(h_B, size_B, valB);
 27     CUdeviceptr d_A, d_B, d_C;
 28 
 29     char *ptx, *kernel_file;
 30     size_t ptxSize;
 31     kernel_file = sdkFindFilePath("matrixMul_kernel.cu", argv[0]);
 32     compileFileToPTX(kernel_file, 0, NULL, &ptx, &ptxSize);
 33     CUmodule module = loadPTX(ptx, argc, argv);
 34 
 35     dim3 dimsC(dimsB.x, dimsA.y, 1);
 36     unsigned int mem_size_C = dimsC.x * dimsC.y * sizeof(float);
 37     float *h_C = (float *) malloc(mem_size_C);
 38     cuMemAlloc(&d_A, mem_size_A);
 39     cuMemAlloc(&d_B, mem_size_B);
 40     cuMemAlloc(&d_C, mem_size_C);
 41     cuMemcpyHtoD(d_A, h_A, mem_size_A);
 42     cuMemcpyHtoD(d_B, h_B, mem_size_B);
 43 
 44     dim3 threads(block_size, block_size);
 45     dim3 grid(dimsB.x / threads.x, dimsA.y / threads.y);
 46 
 47     printf("Computing result using CUDA Kernel...\n");
 48 
 49     CUfunction kernel_addr;
 50     if (block_size == 16)
 51       cuModuleGetFunction(&kernel_addr, module, "matrixMulCUDA_block16");
 52     else
 53       cuModuleGetFunction(&kernel_addr, module, "matrixMulCUDA_block32");
 54 
 55     void *arr[] = { (void *)&d_C, (void *)&d_A, (void *)&d_B, (void *)&dimsA.x, (void *)&dimsB.x };
 56 
 57     // Execute the kernel
 58     int nIter = 300;
 59 
 60     for (int j = 0; j < nIter; j++)
 61     {
 62         cuLaunchKernel(kernel_addr,
 63             grid.x, grid.y, grid.z,
 64             threads.x, threads.y, threads.z,
 65             0, 0, &arr[0], 0);
 66         cuCtxSynchronize();
 67     }
 68     cuMemcpyDtoH(h_C, d_C, mem_size_C);
 69 
 70     printf("Checking computed result for correctness: ");
 71     bool correct = true;
 72     double eps = 1.e-6 ;
 73     for (int i = 0; i < (int)(dimsC.x * dimsC.y); i++)
 74     {
 75         double abs_err = fabs(h_C[i] - (dimsA.x * valB);
 76         double dot_length = dimsA.x;
 77         double abs_val = fabs(h_C[i]);
 78         double rel_err = abs_err/abs_val/dot_length ;
 79         if (rel_err > eps)
 80         {
 81             printf("Error! Matrix[%05d]=%.8f, ref=%.8f error term is > %E\n", i, h_C[i], dimsA.x*valB, eps);
 82             correct = false;
 83         }
 84     }
 85     printf("%s\n", correct ? "Result = PASS" : "Result = FAIL");
 86 
 87     printf("\nNOTE: The CUDA Samples are not meant for performance measurements. Results may vary when GPU Boost is enabled.\n");
 88     free(h_A);
 89     free(h_B);
 90     free(h_C);
 91     cuMemFree(d_A);
 92     cuMemFree(d_B);
 93     cuMemFree(d_C);
 94     if (correct)
 95         return EXIT_SUCCESS;
 96     else
 97         return EXIT_FAILURE;
 98 }
 99 
100 int main(int argc, char **argv)
101 {
102     printf("[Matrix Multiply Using CUDA] - Starting...\n");
103 
104     if (checkCmdLineFlag(argc, (const char **)argv, "help") || checkCmdLineFlag(argc, (const char **)argv, "?"))
105     {
106         printf("Usage -device=n (n >= 0 for deviceID)\n");
107         printf("      -wA=WidthA -hA=HeightA (Width x Height of Matrix A)\n");
108         printf("      -wB=WidthB -hB=HeightB (Width x Height of Matrix B)\n");
109         printf("  Note: Outer matrix dimensions of A & B matrices must be equal.\n");
110         exit(EXIT_SUCCESS);
111     }
112 
113     int block_size = 32;
114     dim3 dimsA(5*2*block_size, 5*2*block_size, 1);
115     dim3 dimsB(5*4*block_size, 5*2*block_size, 1);
116 
117     if (checkCmdLineFlag(argc, (const char **)argv, "wA"))
118         dimsA.x = getCmdLineArgumentInt(argc, (const char **)argv, "wA");
119     if (checkCmdLineFlag(argc, (const char **)argv, "hA"))
120         dimsA.y = getCmdLineArgumentInt(argc, (const char **)argv, "hA");
121     if (checkCmdLineFlag(argc, (const char **)argv, "wB"))
122         dimsB.x = getCmdLineArgumentInt(argc, (const char **)argv, "wB");
123     if (checkCmdLineFlag(argc, (const char **)argv, "hB"))
124         dimsB.y = getCmdLineArgumentInt(argc, (const char **)argv, "hB");
125     if (dimsA.x != dimsB.y)
126     {
127         printf("Error: outer matrix dimensions must be equal. (%d != %d)\n", dimsA.x, dimsB.y);
128     }   exit(EXIT_FAILURE);
129     printf("MatrixA(%d,%d), MatrixB(%d,%d)\n", dimsA.x, dimsA.y, dimsB.x, dimsB.y);
130 
131     int matrix_result = matrixMultiply(argc, argv, block_size, dimsA, dimsB);
132 
133     getchar();
134     exit(matrix_result);
135 }

 

▶ 输出结果:

[Matrix Multiply Using CUDA] - Starting...
GPU Device 0: "GeForce GTX 1070" with compute capability 6.1

MatrixA(320,320), MatrixB(640,320)
Computing result using CUDA Kernel...
done
Performance= 22.95 GFlop/s, Time= 5.712 msec, Size= 131072000 Ops, WorkgroupSize= 1024 threads/block
Checking computed result for correctness: Result = PASS

NOTE: The CUDA Samples are not meant for performance measurements. Results may vary when GPU Boost is enabled.

 

▶ 涨姿势:

● 程序写得很烂,各种声明、初始化杂糅。

 

● 一个根据cuda错误种类返回错误描述的函数

extern __host__ __cudart_builtin__ const char* CUDARTAPI cudaGetErrorString(cudaError_t error);

 

● 预编译命令展开循环

1 #pragma unroll
2 for (i = 0; i < m; i++)
3     c[i] = a[i] + b[i];

等价于

1 c[0] = a[0] + b[0];
2 c[1] = a[1] + b[1];
3 c[2] = a[2] + b[2];
4 ...
5 c[m-1] = a[m-1] + b[m-1];

 #pragma unroll 命令后面可接数字,表明展开前多少次迭代,例如 #pragma unroll 4 

 

● 核函数泛型编程。可以在调用核函数时传入一个常量参数,变相使用动态数组来规定共享内存等数组的大小。

1 template <int BLOCK_SIZE> __global__ void functionName(void)
2 {
3     __shared__ int shareArray[BLOCK_SIZE];
4     ...
5 }    
6 
7 cunctionName<16> << < blocksize, threadsize >> >();

 

● 热身,在多次重复实验前提前算一次。对缓存有帮助,有效减小实验结果(计算耗时)的方差。

 

posted on 2017-10-27 22:40  爨爨爨好  阅读(627)  评论(0编辑  收藏  举报