我想在CUDA中有一个3d float数组,这是我的代码:
#define SIZE_X 128 //numbers in elements
#define SIZE_Y 128
#define SIZE_Z 128
typedef float VolumeType;
cudaExtent volumeSize = make_cudaExtent(SIZE_X, SIZE_Y, SIZE_Z); //The first argument should be SIZE_X*sizeof(VolumeType)??
float *d_volumeMem;
cutilSafeCall(cudaMalloc((void**)&d_volumeMem, SIZE_X*SIZE_Y*SIZE_Z*sizeof(float)));
.....//assign value to d_volumeMem in GPU
cudaArray *d_volumeArray = 0;
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<VolumeType>();
cutilSafeCall( cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize) );
cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr = make_cudaPitchedPtr((void*)d_volumeMem, SIZE_X*sizeof(VolumeType), SIZE_X, SIZE_Y); //
copyParams.dstArray = d_volumeArray;
copyParams.extent = volumeSize;
copyParams.kin = cudaMemcpyDeviceToDevice;
cutilSafeCall( cudaMemcpy3D(©Params) );
实际上,我的程序运行良好。但我不确定结果是否正确。这是我的问题,在CUDA liberay中,它说make_cudaExtent的第一个参数是“Width in bytes”,另外两个是元素的高度和深度。所以我认为在上面的代码中,第五行应该是
cudaExtent volumeSize = make_cudaExtent(SIZE_X*sizeof(VolumeType), SIZE_Y, SIZE_Z);
但是这样,cutilSafeCall中会出现“invalid argument”错误(cudaMemcpy3D(&amp; copyParams));为什么呢?
另一个难题是strcut cudaExtent,正如CUDA库所述,它的组件宽度代表“引用数组内存时元素的宽度,引用线性内存时的字节数”。所以我认为在我的代码中,当我引用volumeSize.width时,它应该是元素中的数字。但是,如果我使用
cudaExtent volumeSize = make_cudaExtent(SIZE_X*sizeof(VolumeType), SIZE_Y, SIZE_Z);
volumeSize.width将是SIZE_X * sizeof(VolumeType)(128 * 4),即以字节为单位的数字而不是元素中的数字。
在许多CUDA SDK中,他们使用char作为VolumeType,因此他们只使用SIZE_X作为make_cudaExtent中的第一个参数。但我的是浮动的,所以,任何人都可以告诉我,如果我需要使用它创建一个3D数组,哪个是创建cudaExtent的正确方法?非常感谢!
答案 0 :(得分:2)
让我们来看看cudaMemcpy3D
的文档说明了什么:
范围字段定义传输区域的尺寸 元素。如果CUDA阵列正在参与副本,则范围是 根据该数组的元素定义。如果没有CUDA数组 参与副本然后,范围在元素中定义 unsigned char。
以及类似cudaMalloc3DArray
笔记的文档:
所有值都在元素中指定
因此,您需要为两个调用形成的范围需要在元素中具有第一个维度(因为cudaMemcpy3D
中的一个分配是一个数组)。
但您可能在代码中遇到了不同的问题,因为您正在使用d_volumeMem
分配线性内存源cudaMalloc
。 cudaMemcpy3D
期望线性源存储器已经分配了兼容的音调。您的代码只是使用大小的线性分配
SIZE_X*SIZE_Y*SIZE_Z*sizeof(float)
现在可能是您选择的尺寸会为您正在使用的硬件产生兼容的间距,但不能保证它会这样做。我建议使用cudaMalloc3D
来分配线性源内存。围绕您的小代码片段展开的扩展演示可能如下所示:
#include <cstdio>
typedef float VolumeType;
const size_t SIZE_X = 8;
const size_t SIZE_Y = 8;
const size_t SIZE_Z = 8;
const size_t width = sizeof(VolumeType) * SIZE_X;
texture<VolumeType, cudaTextureType3D, cudaReadModeElementType> tex;
__global__ void testKernel(VolumeType * output, int dimx, int dimy, int dimz)
{
int tidx = threadIdx.x + blockIdx.x * blockDim.x;
int tidy = threadIdx.y + blockIdx.y * blockDim.y;
int tidz = threadIdx.z + blockIdx.z * blockDim.z;
float x = float(tidx)+0.5f;
float y = float(tidy)+0.5f;
float z = float(tidz)+0.5f;
size_t oidx = tidx + tidy*dimx + tidz*dimx*dimy;
output[oidx] = tex3D(tex, x, y, z);
}
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
template<typename T>
void init(char * devPtr, size_t pitch, int width, int height, int depth)
{
size_t slicePitch = pitch * height;
int v = 0;
for (int z = 0; z < depth; ++z) {
char * slice = devPtr + z * slicePitch;
for (int y = 0; y < height; ++y) {
T * row = (T *)(slice + y * pitch);
for (int x = 0; x < width; ++x) {
row[x] = T(v++);
}
}
}
}
int main(void)
{
VolumeType *h_volumeMem, *d_output, *h_output;
cudaExtent volumeSizeBytes = make_cudaExtent(width, SIZE_Y, SIZE_Z);
cudaPitchedPtr d_volumeMem;
gpuErrchk(cudaMalloc3D(&d_volumeMem, volumeSizeBytes));
size_t size = d_volumeMem.pitch * SIZE_Y * SIZE_Z;
h_volumeMem = (VolumeType *)malloc(size);
init<VolumeType>((char *)h_volumeMem, d_volumeMem.pitch, SIZE_X, SIZE_Y, SIZE_Z);
gpuErrchk(cudaMemcpy(d_volumeMem.ptr, h_volumeMem, size, cudaMemcpyHostToDevice));
cudaArray * d_volumeArray;
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<VolumeType>();
cudaExtent volumeSize = make_cudaExtent(SIZE_X, SIZE_Y, SIZE_Z);
gpuErrchk( cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize) );
cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr = d_volumeMem;
copyParams.dstArray = d_volumeArray;
copyParams.extent = volumeSize;
copyParams.kind = cudaMemcpyDeviceToDevice;
gpuErrchk( cudaMemcpy3D(©Params) );
tex.normalized = false;
tex.filterMode = cudaFilterModeLinear;
tex.addressMode[0] = cudaAddressModeWrap;
tex.addressMode[1] = cudaAddressModeWrap;
tex.addressMode[2] = cudaAddressModeWrap;
gpuErrchk(cudaBindTextureToArray(tex, d_volumeArray, channelDesc));
size_t osize = 64 * sizeof(VolumeType);
gpuErrchk(cudaMalloc((void**)&d_output, osize));
testKernel<<<1,dim3(4,4,4)>>>(d_output,4,4,4);
gpuErrchk(cudaPeekAtLastError());
h_output = (VolumeType *)malloc(osize);
gpuErrchk(cudaMemcpy(h_output, d_output, osize, cudaMemcpyDeviceToHost));
for(int i=0; i<64; i++)
fprintf(stdout, "%d %f\n", i, h_output[i]);
return 0;
}
您可以自己确认纹理读取的输出与主机上的原始源内存匹配。
答案 1 :(得分:-1)
您的代码正确,因为涉及cudaArray。给数组的channelDesc包含有关浮点大小(4个字节)的信息。您的范围规格。使用“ * sizeof(VolumeType)”可以在两个内存指针之间正确复制(使用srcPtr,dstPtr)。然后,还必须以字节为单位指定srcPos和dstPos,即第一个参数“ * sizeof(VolumeType)”。
3d操作系统仍然可能会出现间距问题,具体取决于GPU /驱动程序。我已经看到了,但是很少(2 ^ n的尺寸应该可以)。您也可以在一个for循环中使用cudaMemCpy2DToArray对其进行细分,因为它应具有更高的音调容忍度。没有cudaMalloc2D,因此2D操作的所有始终正确的音调都由SDK发出。