【问题标题】:memset cuArray for surface memory用于表面内存的 memset cuArray
【发布时间】:2022-01-03 19:11:13
【问题描述】:

假设您有一个cuArray 用于绑定表面对象。

某种形式:

// These are inputs to a function really.
cudaArray* d_cuArrSurf
cudaSurfaceObject_t * surfImage;

const cudaExtent extent = make_cudaExtent(width, height, depth);
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
cudaMalloc3DArray(&d_cuArrSurf, &channelDesc, extent);

// Bind to Surface
cudaResourceDesc    surfRes;
memset(&surfRes, 0, sizeof(cudaResourceDesc));
surfRes.resType = cudaResourceTypeArray;
surfRes.res.array.array  = d_cuArrSurf;

cudaCreateSurfaceObject(surfImage, &surfRes);

现在,我想将此cuArray 初始化为零。显然,cuArray 类型的对象没有 memset。最好的方法是什么?也许有多种选择,有些可能有更好或更差的功能。这些选项是什么?

我能想到

  1. 分配和归零主机内存并使用cudaMemcpy3D() 复制它。

  2. 创建一个初始化内核并用surf3Dwrite()编写它

【问题讨论】:

  • 注意:我现在计划测试这两个选项并将结果报告为答案。如果有明确的方法可以做到这一点,请回答这个问题。如果您能想到不同的方法,请在这里回答或评论,我也会测试和报告。
  • 另一种方法是仅在设备内存中工作,分配设备内存,零设备内存,然后执行 cudaMemcpy3D。它可能比主机->设备复制操作更快。我不确定什么会最快。希望这样的事情对您的代码来说不是一个重大的性能问题,在这种情况下,我会选择任何看起来最简单(编码方面)和最容易维护的方法。
  • @RobertCrovella 确实如此。可能是最快的选择,但需要双倍的设备内存,这在某些问题中可能是个问题。不过,我也会将它添加到我的测试中。
  • 您可以在完成后释放设备内存。但是,是的,如果您正在谈论将 30GB 的 cuda 数组归零,这将不起作用。
  • @RobertCrovella 嗯,是的,我在想,在那里的一小段时间里,你需要双倍的内存。如果那个 cuArray 只是你的 RAM 的一半,你就过去了。在我工作的应用程序中,这并非不合理。

标签: cuda cuda-arrays


【解决方案1】:

您可以展示这些行的示例吗?

这里是一个粗略的例子,大致扩展了previous这个粗略的例子:

$ cat t1648.cu
// Includes, system
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>


__device__ float my_common(float *d, int width, unsigned int x, unsigned int y){

// 200 lines of common code...
  return d[y *width +x];
}




////////////////////////////////////////////////////////////////////////////////
// Kernels
////////////////////////////////////////////////////////////////////////////////
//! Write to a cuArray using surface writes
//! @param gIData input data in global memory
////////////////////////////////////////////////////////////////////////////////
__global__ void WriteKernel(float *gIData, int width, int height,
                                       cudaSurfaceObject_t outputSurface)
{
    // calculate surface coordinates
    unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
    unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
    unsigned int z = blockIdx.z*blockDim.z + threadIdx.z;
    // read from global memory and write to cuarray (via surface reference)
    surf3Dwrite(my_common(gIData, width, x, y),
                outputSurface, x*4, y, z, cudaBoundaryModeTrap);
}

__global__ void WriteKernel(float *gIData, int width, int height,
                                       float *out)
{
    // calculate coordinates
    unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
    unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;

    // read from global memory and write to global memory
    out[y*width+x] = my_common(gIData, width, x, y);
}

__global__ void ReadKernel(float tval, cudaSurfaceObject_t outputSurface)
{
    // calculate surface coordinates
    unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
    unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
    unsigned int z = blockIdx.z*blockDim.z + threadIdx.z;;
    // read from global memory and write to cuarray (via surface reference)
    float val;
    surf3Dread(&val,
                outputSurface, x*4, y, z, cudaBoundaryModeTrap);
    if (val != tval) printf("oops\n");
}


////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
    printf("starting...\n");


    unsigned width = 256;
    unsigned height = 256;
    unsigned depth = 256;
    unsigned int size = depth*width * height * sizeof(float);

    // Allocate device memory for result
    float *dData = NULL;
    cudaMalloc((void **) &dData, size);

    // Allocate array and copy image data
    float *out, *h_out;
    h_out = new float[height*width*depth];
    float tval = 1.0f;
    for (int i = 0; i < height*width*depth; i++) h_out[i] = tval;
    cudaArray* d_cuArrSurf;
    cudaSurfaceObject_t  surfImage;

    const cudaExtent extent = make_cudaExtent(width, height, depth);
    cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
    cudaMalloc3DArray(&d_cuArrSurf, &channelDesc, extent);

    // Bind to Surface
    cudaResourceDesc    surfRes;
    memset(&surfRes, 0, sizeof(cudaResourceDesc));
    surfRes.resType = cudaResourceTypeArray;
    surfRes.res.array.array  = d_cuArrSurf;

    cudaCreateSurfaceObject(&surfImage, &surfRes);

    cudaMalloc(&out, size);
    cudaMemcpy(out, h_out, size, cudaMemcpyHostToDevice);
    dim3 dimBlock(8, 8, 8);
    dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);
    // initialize array
    cudaMemcpy3DParms p = {0};
    p.srcPtr = make_cudaPitchedPtr(out, width*sizeof(out[0]), width, height);
    p.srcPos = make_cudaPos(0,0,0);
    p.dstArray = d_cuArrSurf;
    p.dstPos = make_cudaPos(0,0,0);
    p.extent = make_cudaExtent(width, height, 1);
    p.kind   = cudaMemcpyDefault;
    for (int i = 0; i < depth; i++){
      cudaMemcpy3D(&p);
      p.dstPos = make_cudaPos(0,0, i+1);}

    ReadKernel<<<dimGrid, dimBlock>>>(tval, surfImage);
    WriteKernel<<<dimGrid, dimBlock>>>(dData, width, height, surfImage);
    WriteKernel<<<dimGrid, dimBlock>>>(dData, width, height, out);
    cudaDeviceSynchronize();
}
$ nvcc -o t1648 t1648.cu
$ cuda-memcheck ./t1648
========= CUDA-MEMCHECK
starting...
========= ERROR SUMMARY: 0 errors
$

上面的(总)范围是 256x256x256。所以我选择在cudaMemcpy3D 的 256 次迭代中进行 256x256 传输(每个传输范围)(基本上是每个 z 切片)。它似乎通过了嗅探测试。

我在这里使用 1 作为设备内存的初始化值“只是因为”。如果您想加快速度并初始化为零,请跳过主机->设备副本,只需使用 cudaMemset 将线性内存(3D 传输的源)初始化为零。

【讨论】:

  • 啊,我是个白痴。我有完全相同的代码,但我在p.extent 上犯了一个错误,因为我将cudaMalloc3DArray 的代码重用了。任何状况之下。我认为这是完成我在问题中提出的最快、更清晰的方法,同时使用很少的额外记忆。谢谢,像往常一样。
猜你喜欢
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 2016-01-17
  • 2018-08-21
  • 1970-01-01
相关资源
最近更新 更多