我试图在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吗?若然,怎么做?
答案 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的值中找到行。