2013-04-22 120 views
0

讓我從這篇文章道歉開始。我知道有幾個帖子提出了同樣的問題,但我已經嘗試了所給出的解決方案,而且我仍然無法獲得CUDA矩陣乘法的正確結果。CUDA矩陣乘法不正確的結果

從我遵循的示例中,我很確定我的內核算法是正確的。我不相信將2D數組傳遞給內核時有任何問題,並且當它們通過引用傳遞時,我覺得在數組打印到主機中時,2D解決方案數組應該包含正確的答案,但事實並非如此。

難道這是我的dim3 dimGrid(B,B)和dim3 dimThreads(T,T)變量的問題嗎?我是CUDA框架的新手,我仍然試圖圍繞它進行研究。任何建議將非常感激。我的代碼如下:

#include <stdio.h> 
#include <cuda.h> 
#include <stdlib.h> 

__global__ void MatMultiply (int *a, int *b, int *c, int N) { 
    int row = blockIdx.y * blockDim.y + threadIdx.y; 
    int col = blockIdx.x * blockDim.x + threadIdx.x; 
    int val = 0; 

    for (int e = 0; e < N; ++e) { 
     val += a[row*N + e] * b[e*N + col]; 
    } 
    c[row*N+col] = val; 
} 

int main(void) { 
    int N, B, T; 

    printf("Input integer for matrix dimension size: "); 
    scanf("%d", &N); 

    printf("Input number of threads in a block: "); 
    scanf("%d", &T); 

    printf("Input number of blocks in a grid: "); 
    scanf("%d", &B); 

    int size = N * N * sizeof(int); 

    int *a, *b, *c; 

    a = (int*)malloc(size); 
    b = (int*)malloc(size); 
    c = (int*)malloc(size); 

    for (int i = 0; i < N; i++) { 
     for (int j = 0; j < N; j++) { 
      a[i*N+j] = j + i*N; 
      b[i*N+j] = j + i*N; 
      c[i*N+j] = j + i*N; 
     } 
    } 

    int *dev_a, *dev_b, *dev_c; 

    cudaMalloc((void**)&dev_a, size); 
    cudaMalloc((void**)&dev_b, size); 
    cudaMalloc((void**)&dev_c, size); 

    cudaMemcpy(dev_a, a, size, cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_b, b, size, cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_c, c, size, cudaMemcpyHostToDevice); 

    dim3 dimGrid(B, B); 
    dim3 dimThreads(T, T); 
    MatMultiply<<<B, T>>>(dev_a,dev_b,dev_c, N); 

    cudaMemcpy(c, dev_c, size, cudaMemcpyDeviceToHost); 


    for (int i = 0; i < N; i++) { 
     for (int j = 0; j < N; j++) { 
      printf("%d\t", b[i*N + j]); 
     } 
     printf("\n"); 
    } 

    free(a); 
    free(b); 
    free(c); 

    cudaFree(dev_a); 
    cudaFree(dev_b); 
    cudaFree(dev_c); 

    return 0; 
} 

再次感謝。

回答

2

您在內核調用中沒有使用dimGriddimThreads變量。相反,您只需啓動一維線程塊的一維網格。

除此之外,你沒有檢查任何錯誤。

+1

此外,最後,您打印出矩陣'b',這是您的輸入矩陣之一。您可能想要打印出'c'。 – 2013-04-22 22:59:52

+0

謝謝。我不知道我是如何錯過的。現在一切似乎都在起作用。 – Chris 2013-04-23 04:23:12

2

所以,這裏的問題似乎是在建立線程和塊和使用threadIdxblockDimgridDim

注意:在標籤實用的解決方案

threadIdx切實解決這個特殊的問題是因爲它的名字說的線程的ID。這意味着該值,或更preciselly它threadIdx.xthreadIdx.y部件將從指定線程計數的0值,或每塊的值而線程被存儲在blockDim.x去blockDim.y。例如,一個呼叫

someKernel<<<1,32>>>(....); 

將導致threadIdx.x從0到31和threadIdx.y值會不會在所有被迭代(我相信它將永遠是0)。

不過,若你定義一個特定的CUDA結構爲dim3並調用它threadsPerBlock,然後用它作爲第二個參數是這樣的:

dim3 threadsPerBlock(32, 32); 

someKernel<<<1,threadsPerBlock>>>(....); 

,那麼你會得到兩個threadIdx.xthreadIdx.y從0到31在內核執行中獲取它們的各種組合。

請注意,您被限制爲每個啓動塊的最大線程數。這個數字對於不同的顯卡來說是不同的,或者更確切地說是它們支持的計算能力。在this link末尾的表格中查找這些數字因此,計算能力2.x和更高版本支持每塊最多1024個線程,而早期版本支持512.還要注意,這意味着啓動時每個塊最多可以有32x32個線程在2個維度。

但是,如果你需要更多的東西呢?那麼兒子,那麼你啓動更多的塊!您也可以在1維或2維中啓動塊。例如

dim3 threadsPerBlock(32, 32); 
dim3 blocksPerGrid (256, 265); 

someKernel <<<blocksPerGrid,threadsPerBlock>>>(...); 

網格的大小存儲在gridDim結構和在這種情況下,兩個gridDim.xgridDim.y將是256,使得blockIdx.xblockIdx.y變量從0到255

實用的解決方案:

現在我們知道了,讓我們看看你的代碼。在你的代碼,如果你例如設置牛逼爲32和是256,你將有效地得到這樣的:

threadIdx.x would go from 0 to 31 
threadIdx.y would go from 0 to 0 
blockIdx.x would go from 0 to 255 
blockIdx.y would go from 0 to 0 
blockDim.x would be 32 
blockDim.y would be 1 
gridDim.x would be 256 
gridDim.y would be 1 

現在讓我們看看你的變量是如何應對這個...

row would go from 0 to 0 
col would go from 0 to 1023 

所以,這大概不是你想要的。你希望你的行和列都從0到N-1對不對?那麼,這是你如何做到這一點:

int row = threadIdx.x + blockIdx.x * blockDim.x; 
int col = threadIdx.y + blockIdx.y * blockDim.y; 

此外,請確保您有足夠的線程來覆蓋矩陣的尺寸。這是確保你設置* threadsPerBlock * blocksPerGrid *大於您的N。這通常是最好的做法是這樣的:

threads = 32 
dim3 threadsPerBlock (threads, threads); 
blocks = (N/threads) + 1; 
dim3 blocksPerGrid (blocks, blocks); 

「但如果我讓它大於N,那麼我可能有一些線程我不需要」 - 說你 - 「我不希望他們做的工作!」明智的你是先生,這樣說。您可以通過簡單的解決這個問題,如果條款中,你會附上你的計算,像這樣:

if (row < N && col < N) 
{ 
    // your add... err... code here 
} 

希望有所幫助。享受CUDA;)