用于多GPU纹理代码的cudaArray数组

用于多GPU纹理代码的cudaArray数组,cuda,Cuda,我有一些代码,我正试图为一个通用的多GPU案例工作,用于n数量相等的设备,其中n在编译时是未知的 对于这段代码,我需要绑定到纹理内存一些数组,我需要完全相同的数据绑定到不同的GPU 用于3D纹理绑定的单个GPU内存代码如下所示: cudaArray *d_imagedata = 0; const cudaExtent extent = make_cudaExtent(geo.nVoxelX, geo.nVoxelY, geo.nVoxelZ); cudaChannelFormatDesc cha

我有一些代码,我正试图为一个通用的多GPU案例工作,用于
n
数量相等的设备,其中
n
在编译时是未知的

对于这段代码,我需要绑定到纹理内存一些数组,我需要完全相同的数据绑定到不同的GPU

用于3D纹理绑定的单个GPU内存代码如下所示:

cudaArray *d_imagedata = 0;
const cudaExtent extent = make_cudaExtent(geo.nVoxelX, geo.nVoxelY, geo.nVoxelZ);
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
cudaMalloc3DArray(&d_imagedata, &channelDesc, extent);
cudaCheckErrors("cudaMalloc3D error 3D tex");

cudaMemcpy3DParms copyParams = { 0 };
copyParams.srcPtr = make_cudaPitchedPtr((void*)img, extent.width*sizeof(float), extent.width, extent.height);
copyParams.dstArray = d_imagedata;
copyParams.extent = extent;
copyParams.kind = cudaMemcpyHostToDevice;
cudaMemcpy3D(&copyParams);

cudaCheckErrors("cudaMemcpy3D fail");

// Configure texture options
tex.normalized = false;
tex.filterMode = cudaFilterModePoint; 
tex.addressMode[0] = cudaAddressModeBorder;
tex.addressMode[1] = cudaAddressModeBorder;
tex.addressMode[2] = cudaAddressModeBorder;

cudaBindTextureToArray(tex, d_imagedata, channelDesc);
cudaArray*d_imagedata=0;
const cudaExtent extent=make_cudaExtent(geo.nVoxelX,geo.nVoxelY,geo.nVoxelZ);
cudaChannelFormatDesc channelDesc=cudaCreateChannelDesc();
cudaMalloc3DArray(&d_图像数据和channelDesc,范围);
CUDACHECKERRS(“cudaMalloc3D错误3D特克斯”);
cudaMemcpy3DParms copyParams={0};
copyParams.srcPtr=make_cudaPitchedPtr((void*)img,extent.width*sizeof(float),extent.width,extent.height);
copyParams.dstArray=d_imagedata;
copyParams.extent=范围;
copyParams.kind=cudamemcpyhostodevice;
cudaMemcpy3D(和copyParams);
CUDACHECKERS(“cudaMemcpy3D失败”);
//配置纹理选项
tex.normalized=false;
tex.filterMode=cudaFilterModePoint;
tex.addressMode[0]=cudaAddressModeBorder;
tex.addressMode[1]=cudaAddressModeBorder;
tex.addressMode[2]=cudaAddressModeBorder;
cudaBindTextureToArray(tex、d_imagedata、channelDesc);
这是一个标准的复制到
cudaArray
的过程,然后是绑定和设置过程,这里没有什么新东西

要将此代码转换为多GPU,我知道我不需要更改
tex
全局纹理参考,因为CUDA将知道不同的GPU具有不同的
tex
,但是我确实需要
n
cudaArray*d_imagedata
实例,每个GPU一个

如何创建(和分配)一个
cudaArray
s数组


如果是全局内存指针,那就更容易了,只需在双指针上安装一个CPU
malloc
,然后在每个指针上安装
cudamaloc
,就可以了,但由于
cudaArray
不是标准类型,我还没有想出如何用它创建灵活的数组

我建议使用纹理对象,而不是纹理引用

使用,对所示代码的一个小修改似乎对我来说是正确的:

$ cat t341.cu
#include <helper_cuda.h>
#include <curand.h>
#define NUM_TEX 4

const int SizeNoiseTest = 32;
const int cubeSizeNoiseTest = SizeNoiseTest*SizeNoiseTest*SizeNoiseTest;
static cudaTextureObject_t texNoise[NUM_TEX];

__global__ void AccesTexture(cudaTextureObject_t my_tex)
{
        float test = tex3D<float>(my_tex,(float)threadIdx.x,(float)threadIdx.y,(float)threadIdx.z);//by using this the error occurs
        printf("thread: %d,%d,%d, value: %f\n", threadIdx.x, threadIdx.y, threadIdx.z, test);
}

void CreateTexture()
{

    for (int i = 0; i < NUM_TEX; i++){
        cudaSetDevice(i);
        float *d_NoiseTest;//Device Array with random floats
        cudaMalloc((void **)&d_NoiseTest, cubeSizeNoiseTest*sizeof(float));//Allocation of device Array
        //curand Random Generator (needs compiler link -lcurand)
        curandGenerator_t gen;
        curandCreateGenerator(&gen,CURAND_RNG_PSEUDO_DEFAULT);
        curandSetPseudoRandomGeneratorSeed(gen,1235ULL+i);
        curandGenerateUniform(gen, d_NoiseTest, cubeSizeNoiseTest);//writing data to d_NoiseTest
        curandDestroyGenerator(gen);

        //cudaArray Descriptor
        cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
        //cuda Array
        cudaArray *d_cuArr;
        checkCudaErrors(cudaMalloc3DArray(&d_cuArr, &channelDesc, make_cudaExtent(SizeNoiseTest*sizeof(float),SizeNoiseTest,SizeNoiseTest), 0));
        cudaMemcpy3DParms copyParams = {0};


        //Array creation
        copyParams.srcPtr   = make_cudaPitchedPtr(d_NoiseTest, SizeNoiseTest*sizeof(float), SizeNoiseTest, SizeNoiseTest);
        copyParams.dstArray = d_cuArr;
        copyParams.extent   = make_cudaExtent(SizeNoiseTest,SizeNoiseTest,SizeNoiseTest);
        copyParams.kind     = cudaMemcpyDeviceToDevice;
        checkCudaErrors(cudaMemcpy3D(&copyParams));
        //Array creation End

        cudaResourceDesc    texRes;
        memset(&texRes, 0, sizeof(cudaResourceDesc));
        texRes.resType = cudaResourceTypeArray;
        texRes.res.array.array  = d_cuArr;
        cudaTextureDesc     texDescr;
        memset(&texDescr, 0, sizeof(cudaTextureDesc));
        texDescr.normalizedCoords = false;
        texDescr.filterMode = cudaFilterModeLinear;
        texDescr.addressMode[0] = cudaAddressModeClamp;   // clamp
        texDescr.addressMode[1] = cudaAddressModeClamp;
        texDescr.addressMode[2] = cudaAddressModeClamp;
        texDescr.readMode = cudaReadModeElementType;
        checkCudaErrors(cudaCreateTextureObject(&texNoise[i], &texRes, &texDescr, NULL));}
}

int main(int argc, char **argv)
{
        CreateTexture();
        cudaSetDevice(0);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[0]);
        cudaSetDevice(1);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[1]);
        cudaSetDevice(2);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[2]);
        checkCudaErrors(cudaPeekAtLastError());
        cudaSetDevice(0);
        checkCudaErrors(cudaDeviceSynchronize());
        cudaSetDevice(1);
        checkCudaErrors(cudaDeviceSynchronize());
        cudaSetDevice(2);
        checkCudaErrors(cudaDeviceSynchronize());
        return 0;
}
$ nvcc -arch=sm_30 -I/usr/local/cuda/samples/common/inc -lcurand -o t341 t341.cu
$ cuda-memcheck ./t341
========= CUDA-MEMCHECK
thread: 0,0,0, value: 0.310691
thread: 1,0,0, value: 0.627906
thread: 0,1,0, value: 0.638900
thread: 1,1,0, value: 0.665186
thread: 0,0,1, value: 0.167465
thread: 1,0,1, value: 0.565227
thread: 0,1,1, value: 0.397606
thread: 1,1,1, value: 0.503013
thread: 0,0,0, value: 0.809163
thread: 1,0,0, value: 0.795669
thread: 0,1,0, value: 0.808565
thread: 1,1,0, value: 0.847564
thread: 0,0,1, value: 0.853998
thread: 1,0,1, value: 0.688446
thread: 0,1,1, value: 0.733255
thread: 1,1,1, value: 0.649379
thread: 0,0,0, value: 0.040824
thread: 1,0,0, value: 0.087417
thread: 0,1,0, value: 0.301392
thread: 1,1,0, value: 0.298669
thread: 0,0,1, value: 0.161962
thread: 1,0,1, value: 0.316443
thread: 0,1,1, value: 0.452077
thread: 1,1,1, value: 0.477722
========= ERROR SUMMARY: 0 errors
$

此代码在具有(至少)3个GPU的系统上运行。我还更新了上面的示例,以演示如何创建指向
cudaArray
类型的指针数组,并演示如何避免内存泄漏。

我建议使用纹理对象,而不是纹理引用

使用,对所示代码的一个小修改似乎对我来说是正确的:

$ cat t341.cu
#include <helper_cuda.h>
#include <curand.h>
#define NUM_TEX 4

const int SizeNoiseTest = 32;
const int cubeSizeNoiseTest = SizeNoiseTest*SizeNoiseTest*SizeNoiseTest;
static cudaTextureObject_t texNoise[NUM_TEX];

__global__ void AccesTexture(cudaTextureObject_t my_tex)
{
        float test = tex3D<float>(my_tex,(float)threadIdx.x,(float)threadIdx.y,(float)threadIdx.z);//by using this the error occurs
        printf("thread: %d,%d,%d, value: %f\n", threadIdx.x, threadIdx.y, threadIdx.z, test);
}

void CreateTexture()
{

    for (int i = 0; i < NUM_TEX; i++){
        cudaSetDevice(i);
        float *d_NoiseTest;//Device Array with random floats
        cudaMalloc((void **)&d_NoiseTest, cubeSizeNoiseTest*sizeof(float));//Allocation of device Array
        //curand Random Generator (needs compiler link -lcurand)
        curandGenerator_t gen;
        curandCreateGenerator(&gen,CURAND_RNG_PSEUDO_DEFAULT);
        curandSetPseudoRandomGeneratorSeed(gen,1235ULL+i);
        curandGenerateUniform(gen, d_NoiseTest, cubeSizeNoiseTest);//writing data to d_NoiseTest
        curandDestroyGenerator(gen);

        //cudaArray Descriptor
        cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
        //cuda Array
        cudaArray *d_cuArr;
        checkCudaErrors(cudaMalloc3DArray(&d_cuArr, &channelDesc, make_cudaExtent(SizeNoiseTest*sizeof(float),SizeNoiseTest,SizeNoiseTest), 0));
        cudaMemcpy3DParms copyParams = {0};


        //Array creation
        copyParams.srcPtr   = make_cudaPitchedPtr(d_NoiseTest, SizeNoiseTest*sizeof(float), SizeNoiseTest, SizeNoiseTest);
        copyParams.dstArray = d_cuArr;
        copyParams.extent   = make_cudaExtent(SizeNoiseTest,SizeNoiseTest,SizeNoiseTest);
        copyParams.kind     = cudaMemcpyDeviceToDevice;
        checkCudaErrors(cudaMemcpy3D(&copyParams));
        //Array creation End

        cudaResourceDesc    texRes;
        memset(&texRes, 0, sizeof(cudaResourceDesc));
        texRes.resType = cudaResourceTypeArray;
        texRes.res.array.array  = d_cuArr;
        cudaTextureDesc     texDescr;
        memset(&texDescr, 0, sizeof(cudaTextureDesc));
        texDescr.normalizedCoords = false;
        texDescr.filterMode = cudaFilterModeLinear;
        texDescr.addressMode[0] = cudaAddressModeClamp;   // clamp
        texDescr.addressMode[1] = cudaAddressModeClamp;
        texDescr.addressMode[2] = cudaAddressModeClamp;
        texDescr.readMode = cudaReadModeElementType;
        checkCudaErrors(cudaCreateTextureObject(&texNoise[i], &texRes, &texDescr, NULL));}
}

int main(int argc, char **argv)
{
        CreateTexture();
        cudaSetDevice(0);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[0]);
        cudaSetDevice(1);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[1]);
        cudaSetDevice(2);
        AccesTexture<<<1,dim3(2,2,2)>>>(texNoise[2]);
        checkCudaErrors(cudaPeekAtLastError());
        cudaSetDevice(0);
        checkCudaErrors(cudaDeviceSynchronize());
        cudaSetDevice(1);
        checkCudaErrors(cudaDeviceSynchronize());
        cudaSetDevice(2);
        checkCudaErrors(cudaDeviceSynchronize());
        return 0;
}
$ nvcc -arch=sm_30 -I/usr/local/cuda/samples/common/inc -lcurand -o t341 t341.cu
$ cuda-memcheck ./t341
========= CUDA-MEMCHECK
thread: 0,0,0, value: 0.310691
thread: 1,0,0, value: 0.627906
thread: 0,1,0, value: 0.638900
thread: 1,1,0, value: 0.665186
thread: 0,0,1, value: 0.167465
thread: 1,0,1, value: 0.565227
thread: 0,1,1, value: 0.397606
thread: 1,1,1, value: 0.503013
thread: 0,0,0, value: 0.809163
thread: 1,0,0, value: 0.795669
thread: 0,1,0, value: 0.808565
thread: 1,1,0, value: 0.847564
thread: 0,0,1, value: 0.853998
thread: 1,0,1, value: 0.688446
thread: 0,1,1, value: 0.733255
thread: 1,1,1, value: 0.649379
thread: 0,0,0, value: 0.040824
thread: 1,0,0, value: 0.087417
thread: 0,1,0, value: 0.301392
thread: 1,1,0, value: 0.298669
thread: 0,0,1, value: 0.161962
thread: 1,0,1, value: 0.316443
thread: 0,1,1, value: 0.452077
thread: 1,1,1, value: 0.477722
========= ERROR SUMMARY: 0 errors
$

此代码在具有(至少)3个GPU的系统上运行。我还更新了上面的示例,因此它演示了如何创建指向
cudaArray
类型的指针数组,并演示了如何避免内存泄漏。

谢谢,这似乎是一种方法。只有一个问题:使用这个系统,你需要知道编译时系统上GPU的数量?您有4个
NUM_TEX
,但在测试中仅使用3个设备。这是因为您没有安装4台设备,因此没有第四次创建纹理吗?我只是不确定我是否遗漏了什么,所以这一切都不应该依赖于编译时。我在答案中添加了一个变体来证明这一点。啊,我现在明白了,我对这个全局定义有点困惑。谢谢,像往常一样回答得很好!嗨,罗伯特,这是一个跟我原来的问题有点关联的后续问题。在这里,您并没有存储
d_cuArr
的每个实例,正如您明确提到的,但为了安全编码,我需要存储这些实例,并在不需要时适当地释放它们。这再次引发了标题中的问题,我如何创建这些元素的数组,以便稍后正确地释放它们?我知道如何进行
d_NoiseTest
,但由于其
cudaArray
类型,我不知道如何进行
d_cuArr
。没问题。在某种程度上,这些东西迫使我写出更好的答案。现在,将来很少有人会说“你知道你在这里发布的代码有内存泄漏吗…”并且可以说
d_NoiseTest
数组可以封装在create函数中。它实际上不需要来回传递到
main
,除非您希望该数据用于其他用途。谢谢,这似乎是一种方法。只有一个问题:使用这个系统,你需要知道编译时系统上GPU的数量?您有4个
NUM_TEX
,但在测试中仅使用3个设备。这是因为您没有安装4台设备,因此没有第四次创建纹理吗?我只是不确定我是否遗漏了什么,所以这一切都不应该依赖于编译时。我在答案中添加了一个变体来证明这一点。啊,我现在明白了,我对这个全局定义有点困惑。谢谢,像往常一样回答得很好!嗨,罗伯特,这是一个跟我原来的问题有点关联的后续问题。在这里,您并没有存储
d_cuArr
的每个实例,正如您明确提到的,但为了安全编码,我需要存储这些实例,并在不需要时适当地释放它们。这再次引发了标题中的问题,我如何创建这些元素的数组,以便稍后正确地释放它们?我知道如何进行
d_NoiseTest
,但由于其
cudaArray
类型,我不知道如何进行
d_cuArr
。没问题。在某种程度上,这些东西迫使我写出更好的答案。现在,将来很少有人会说“你知道你在这里发布的代码有内存泄漏吗…”并且可以说
d_NoiseTest
数组可以封装在create函数中。它实际上不需要来回传递到
main
,除非您希望该数据用于其他用途。