gpt4 book ai didi

c - 将 3d 数组发送到 CUDA 内核

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:50:28 25 4
gpt4 key购买 nike

我将给出的代码作为 How can I add up two 2d (pitched) arrays using nested for loops? 的答案并尝试将其用于 3D 而不是 2D 并稍微更改了其他部分,现在它看起来如下:

 __global__ void doSmth(int*** a) {
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
for(int k=0; k<2; k++)
a[i][j][k]=i+j+k;
}

int main() {
int*** h_c = (int***) malloc(2*sizeof(int**));
for(int i=0; i<2; i++) {
h_c[i] = (int**) malloc(2*sizeof(int*));
for(int j=0; j<2; j++)
GPUerrchk(cudaMalloc((void**)&h_c[i][j],2*sizeof(int)));
}
int*** d_c;
GPUerrchk(cudaMalloc((void****)&d_c,2*sizeof(int**)));
GPUerrchk(cudaMemcpy(d_c,h_c,2*sizeof(int**),cudaMemcpyHostToDevice));
doSmth<<<1,1>>>(d_c);
GPUerrchk(cudaPeekAtLastError());

int res[2][2][2];
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
GPUerrchk(cudaMemcpy(&res[i][j][0],
h_c[i][j],2*sizeof(int),cudaMemcpyDeviceToHost));

for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
for(int k=0; k<2; k++)
printf("[%d][%d][%d]=%d\n",i,j,k,res[i][j][k]);
}

在上面的代码中,我使用 2 作为 h_c 的每个维度的大小,在实际实现中,我将拥有非常大的这些大小,并且对于“int***”的子数组的每个部分都有不同的大小或更多维度。我在内核调用后遇到问题,我尝试将结果复制回 res 数组。你能帮我解决这个问题吗? Plz 你能按照我上面写的方式展示解决方案吗?谢谢!

最佳答案

首先,我认为 talonmies 在发布对您提到的上一个问题的回复时,并不打算将其作为良好编码的代表。因此,弄清楚如何将其扩展到 3D 可能不是对您时间的最佳利用。例如,为什么我们要编写只使用一个线程的程序?虽然这样的内核可能有合法用途,但这不是其中之一。您的内核有可能并行 执行大量独立工作,但您却将其全部强制到一个线程上,并对其进行序列化。并行工作的定义是:

a[i][j][k]=i+j+k;

让我们弄清楚如何在 GPU 上并行处理它。

我要进行的另一个介绍性观察是,由于我们正在处理的问题的大小是提前已知的,所以让我们使用 C 来解决这些问题,并尽可能多地从该语言中获得好处。在某些情况下可能需要嵌套循环来执行 cudaMalloc,但我认为这不是其中之一。

这是并行完成工作的代码:

#include <stdio.h>
#include <stdlib.h>
// set a 3D volume
// To compile it with nvcc execute: nvcc -O2 -o set3d set3d.cu
//define the data set size (cubic volume)
#define DATAXSIZE 100
#define DATAYSIZE 100
#define DATAZSIZE 20
//define the chunk sizes that each threadblock will work on
#define BLKXSIZE 32
#define BLKYSIZE 4
#define BLKZSIZE 4

// for cuda error checking
#define cudaCheckErrors(msg) \
do { \
cudaError_t __err = cudaGetLastError(); \
if (__err != cudaSuccess) { \
fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
msg, cudaGetErrorString(__err), \
__FILE__, __LINE__); \
fprintf(stderr, "*** FAILED - ABORTING\n"); \
return 1; \
} \
} while (0)

// device function to set the 3D volume
__global__ void set(int a[][DATAYSIZE][DATAXSIZE])
{
unsigned idx = blockIdx.x*blockDim.x + threadIdx.x;
unsigned idy = blockIdx.y*blockDim.y + threadIdx.y;
unsigned idz = blockIdx.z*blockDim.z + threadIdx.z;
if ((idx < (DATAXSIZE)) && (idy < (DATAYSIZE)) && (idz < (DATAZSIZE))){
a[idz][idy][idx] = idz+idy+idx;
}
}

int main(int argc, char *argv[])
{
typedef int nRarray[DATAYSIZE][DATAXSIZE];
const dim3 blockSize(BLKXSIZE, BLKYSIZE, BLKZSIZE);
const dim3 gridSize(((DATAXSIZE+BLKXSIZE-1)/BLKXSIZE), ((DATAYSIZE+BLKYSIZE-1)/BLKYSIZE), ((DATAZSIZE+BLKZSIZE-1)/BLKZSIZE));
// overall data set sizes
const int nx = DATAXSIZE;
const int ny = DATAYSIZE;
const int nz = DATAZSIZE;
// pointers for data set storage via malloc
nRarray *c; // storage for result stored on host
nRarray *d_c; // storage for result computed on device
// allocate storage for data set
if ((c = (nRarray *)malloc((nx*ny*nz)*sizeof(int))) == 0) {fprintf(stderr,"malloc1 Fail \n"); return 1;}
// allocate GPU device buffers
cudaMalloc((void **) &d_c, (nx*ny*nz)*sizeof(int));
cudaCheckErrors("Failed to allocate device buffer");
// compute result
set<<<gridSize,blockSize>>>(d_c);
cudaCheckErrors("Kernel launch failure");
// copy output data back to host

cudaMemcpy(c, d_c, ((nx*ny*nz)*sizeof(int)), cudaMemcpyDeviceToHost);
cudaCheckErrors("CUDA memcpy failure");
// and check for accuracy
for (unsigned i=0; i<nz; i++)
for (unsigned j=0; j<ny; j++)
for (unsigned k=0; k<nx; k++)
if (c[i][j][k] != (i+j+k)) {
printf("Mismatch at x= %d, y= %d, z= %d Host= %d, Device = %d\n", i, j, k, (i+j+k), c[i][j][k]);
return 1;
}
printf("Results check!\n");
free(c);
cudaFree(d_c);
cudaCheckErrors("cudaFree fail");
return 0;
}

由于您已在评论中提出要求,因此我可以对您的代码进行最少的更改以使其正常工作。让我们也提醒自己您引用的上一个问题中的一些爪牙评论:

“出于代码复杂性和性能原因,您真的不想这样做,在 CUDA 代码中使用指针数组比使用线性内存的替代方案更难也更慢。”

“与使用线性内存相比,这是一个糟糕的主意。”

我必须在纸上画出这个图,以确保我的所有指针复制正确。

#include <cstdio>
inline void GPUassert(cudaError_t code, char * file, int line, bool Abort=true)
{
if (code != 0) {
fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code),file,line);
if (Abort) exit(code);
}
}

#define GPUerrchk(ans) { GPUassert((ans), __FILE__, __LINE__); }



__global__ void doSmth(int*** a) {
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
for(int k=0; k<2; k++)
a[i][j][k]=i+j+k;
}
int main() {
int*** h_c = (int***) malloc(2*sizeof(int**));
for(int i=0; i<2; i++) {
h_c[i] = (int**) malloc(2*sizeof(int*));
for(int j=0; j<2; j++)
GPUerrchk(cudaMalloc((void**)&h_c[i][j],2*sizeof(int)));
}
int ***h_c1 = (int ***) malloc(2*sizeof(int **));
for (int i=0; i<2; i++){
GPUerrchk(cudaMalloc((void***)&(h_c1[i]), 2*sizeof(int*)));
GPUerrchk(cudaMemcpy(h_c1[i], h_c[i], 2*sizeof(int*), cudaMemcpyHostToDevice));
}
int*** d_c;
GPUerrchk(cudaMalloc((void****)&d_c,2*sizeof(int**)));
GPUerrchk(cudaMemcpy(d_c,h_c1,2*sizeof(int**),cudaMemcpyHostToDevice));
doSmth<<<1,1>>>(d_c);
GPUerrchk(cudaPeekAtLastError());
int res[2][2][2];
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
GPUerrchk(cudaMemcpy(&res[i][j][0], h_c[i][j],2*sizeof(int),cudaMemcpyDeviceToHost));

for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
for(int k=0; k<2; k++)
printf("[%d][%d][%d]=%d\n",i,j,k,res[i][j][k]);
}

简而言之,我们必须执行一系列连续操作:

  1. malloc 指针的多维数组(在主机上),比问题大小小一维,最后一维是指向 cudaMalloc 到设备而不是主机上的区域的一组指针。
  2. 创建另一个多维指针数组,与上一步中创建的类相同,但比上一步中创建的小一维。此数组还必须在设备上具有最终等级 cudaMalloc。
  3. 将上一步中的最后一组主机指针复制到上一步中设备上的 cudaMalloced 区域。
  4. 重复步骤 2-3,直到我们得到一个指向多维指针数组的单个(主机)指针,所有这些指针现在都驻留在设备上。

关于c - 将 3d 数组发送到 CUDA 内核,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29902373/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com