CUDA网格跨越2D阵列

时间:2014-03-23 16:56:03

标签: arrays cuda

我试图在CUDA上有效地循环二维数组。在主机代码中我有

double **h_matrix; // Matrix on host of size Nx by Ny
double tmp;
...
for(i = 0; i < Nx; i++) {
    for(j = 0; j < Ny; j++) {
        tmp = h_matrix[i][j];
        ... // Perform some operation on tmp
        h_matrix[i][j] = tmp;
    }
}

为了在CUDA中有效地执行类似的任务,我理解我必须使用cudaMallocPitch()为2D数组分配内存,如CUDA Programming guide所示(例如滚动一下)。该示例实际上没有多大帮助,因为即使它以<<<100, 512>>>的形式启动,该内核也不会使用有关执行它的网格,块或线程的任何信息。

NVidia'a Parallel forall blog suggests使用网格步幅循环来编写灵活的&amp;但是,可扩展内核的示例仅使用1D数组。如何为使用cudaMallocPitch()分配的2D数组编写网格步长循环以并行化上面显示的代码?我应该使用2D dimGrid和dimBlock吗?若然,怎么做?

3 个答案:

答案 0 :(得分:5)

这是我根据JackOLantern的答案创建的一个完整的可编辑示例。

#include <stdio.h>
#include <assert.h>

#define N 11
#define M 3

__global__ void kernel(float * d_matrix, size_t pitch) {
    for (int j = blockIdx.y * blockDim.y + threadIdx.y; j < N; j += blockDim.y * gridDim.y) {
        float* row_d_matrix = (float*)((char*)d_matrix + j*pitch);
        for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M; i += blockDim.x * gridDim.x) {
            row_d_matrix[i] = (j * M + i) + (j * M + i);
        }
    }
}

void verify(float *h, float *d, int size) {
    for (int i = 0; i < size; i++) {
        assert(h[i] == d[i]);
    }
    printf("Results match\n");
}

int main() {

    float *h_matrix;
    float *d_matrix;
    float *dc_matrix;

    h_matrix = (float *) malloc(M * N * sizeof(float));
    dc_matrix = (float *) malloc(M * N * sizeof(float));

    for (int j = 0; j < N; j++) {
        for (int i = 0; i < M; i++) {
            h_matrix[j * M + i] = (j * M + i) + (j * M + i);
        }
    }

    size_t pitch;
    cudaMallocPitch(&d_matrix, &pitch, M * sizeof(float), N);

    dim3 grid(1, 1, 1);
    dim3 block(3, 3, 1);

    kernel<<<grid, block>>>(d_matrix, pitch);

    cudaMemcpy2D(dc_matrix, M * sizeof(float), d_matrix, pitch, M * sizeof(float), N, cudaMemcpyDeviceToHost);

    verify(h_matrix, dc_matrix, M * N);

    free(h_matrix);
    cudaFree(d_matrix);
    free(dc_matrix);
}

答案 1 :(得分:4)

对于与cudaMallocPitch分配的2D矩阵相关的网格跨步循环概念到2D情况的扩展可能看起来像:

#define N 11
#define M 3

__global__ void kernel(float * d_matrix, size_t pitch) {

    int idx = blockIdx.x*blockDim.x + threadIdx.x;
    int idy = blockIdx.y*blockDim.y + threadIdx.y;

    for (int j = blockIdx.y * blockDim.y + threadIdx.y; j < N; j += blockDim.y * gridDim.y) 
    {
        float* row_d_matrix = (float*)((char*)d_matrix + idy*pitch);
        for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < M; i += blockDim.x * gridDim.x) {
            row_d_matrix[i] = ....
       } 

    }

}

int main()
{

    float *d_matrix;

    size_t pitch;
    cudaMallocPitch(&d_matrix,&pitch,M*sizeof(float),N);

    kernel<<<GridSize,BlockSize>>>(d_matrix,pitch);

    // Other stuff

}

答案 2 :(得分:4)

这是一个古老的问题,但我有一些建议可以改善已接受的答案。由于该部分已被覆盖,我有点无视音高部分。

接受的答案,虽然它遍历所有值,但没有考虑线程之间的任何类型的平衡。

让我们举个小例子。假设我们使用<<<1, block>>>启动内核,其中块为dim3 block(2,2)。然后我们正在研究5x5矩阵。现在按照上面的建议,工作分配最终会使id(0,0)的线程获得9次运行,而线程(0,1)和(1,0)每次获得6次(1, 1)总共获得4次。

所以我更好地平衡负载的建议是平整循环并计算扁平环的指数。

所以我的建议更像是

int n=11;
int m=3;
int i, j, k;
for(i = (blockIdx.y * blockDim.y + threadIdx.y) * blockDim.x * gridDim.x + 
        (blockIdx.x * blockDim.x + threadIdx.x); 
    i < m*n; 
    i += blockDim.y * gridDim.y * blockDim.x * gridDim.x) {
  j = i/m;
  k = i%m;
  //Calculations here
}

这将适用于上面关于音高的例子,你可以从j的值中找到行。