我用CUDA写了一小段代码来乘以2个方阵。 Hovewer,事实证明大多数细胞计算错误。根据我用过的教程,一切都应该没问题。
__global__ void gpuMM(int *C, int *A, int *B, int N)
{
int row = blockIdx.x*blockDim.x + threadIdx.x;
int col = blockIdx.y*blockDim.y + threadIdx.y;
int sum = 0;
for (int n = 0; n < N; ++n)
sum += A[row*N+n]*B[n*N+col];
C[row*N+col] = sum;
}
#define ROW_SIZE 5
#define MATRIX_LENGTH ROW_SIZE*ROW_SIZE
#define BLOCK_SIZE 16
void MultiplyMatrixCUDA(int * pResult, int* pFactorA, int*pFactorB)
{
int size = MATRIX_LENGTH*sizeof(int);
int *dA,*dB,*dC;
cudaMalloc(&dA,size);
cudaMalloc(&dB,size);
cudaMalloc(&dC,size);
int K = 100;
dim3 threadBlock(BLOCK_SIZE,BLOCK_SIZE);
dim3 grid(K,K);
printf("A:\n");
DrawMatrix(pFactorA);
printf("\n");
printf("B:\n");
DrawMatrix(pFactorB);
printf("\n");
// Copy matrices from the host to device
cudaMemcpy(dA,pFactorA,size,cudaMemcpyHostToDevice);
cudaMemcpy(dB,pFactorB,size,cudaMemcpyHostToDevice);
//Execute the matrix multiplication kernel
gpuMM<<<grid,threadBlock>>>(dC,dA,dB,ROW_SIZE);
// Allocate memory to store the GPU answer on the host
int *C;
C = new int[MATRIX_LENGTH];
// Now copy the GPU result back to CPU
cudaMemcpy(C,dC,size,cudaMemcpyDeviceToHost);
cudaFree(dA);
cudaFree(dB);
cudaFree(dC);
printf("\nC from CUDA:\n");
DrawMatrix(C);
printf("\nC:\n");
DrawMatrix(MultiplyWithCPU(pResult,pFactorA, pFactorB)); // the code of multiplying function is irrevelant, I'm sure it works fine (double-checked)
}
结果显示矩阵乘以标准CPU方法是正确的,但CUDA错误:
第一行总是正确的,但所有其他部分都是完全随机的。有时他们是消极的,有时不是。有时它们接近真实值,有时它们完全不同。
我的错误是什么?我没看到失败的地方。算法看起来很好,变量似乎正确传递,但有些东西不起作用。
---编辑
所有变量(pResult和两个pFactors)都在代码的其他部分初始化(后来被删除)。
答案 0 :(得分:3)
由于每个块的线程数不等于输出矩阵中的元素数(您在16x16块上映射5x5矩阵),因此某些线程正在访问/写入无效的内存位置。
解决方案包括双边界检查以解决问题。这将导致一些线程空闲。内核应如下所示:
__global__ void gpuMM(int *C, int *A, int *B, int N)
{
int row = blockIdx.x*blockDim.x + threadIdx.x;
int col = blockIdx.y*blockDim.y + threadIdx.y;
if( (row < N) && (col < N))
{
int sum = 0;
for (int n = 0; n < N; ++n){
sum += A[row*N+n]*B[n*N+col];
}
C[row*N+col] = sum;
}
}
另一个解决方案 - 更有效,取决于您的设备,确实是 - 每个块启动更少的线程(在这种情况下为25)。