CUDA 3D到线性索引映射(Pitch)

时间:2015-02-27 14:07:35

标签: 3d cuda pitch

我有三维数据,我正在使用CUDA进行处理。

我使用 cudaMallocPitch()分配内存。

cudaMallocPitch((void **)&test_data, &pitch, sizeof(float)*N*N, N);

在我的代码的二维版本中,维度为N * N,我通过这样做访问了一个特定的元素:

i = blockIdx.x*BLOCK_X + threadIdx.x;
j = blockIdx.y*BLOCK_Y + threadIdx.y;
linearIdx = i + j*pitch/sizeof(float);

现在我希望将代码扩展到3-D。我可以类似地获得 z 索引

k = blockIdx.z*BLOCK_Z + threadIdx.z;

但是如何将这三者结合起来获得线性指数呢? pitch 究竟是什么?我现在如何访问该元素?请注释我为3-D数据分配内存的方法是否正确。

谢谢!

2 个答案:

答案 0 :(得分:1)

2D和3D阵列的编程指南中有几个很好的例子。它们如下:

2D:

// Host code
int width = 64, height = 64;
float* devPtr;
size_t pitch;
cudaMallocPitch(&devPtr, &pitch,
                width * sizeof(float), height);
MyKernel<<<100, 512>>>(devPtr, pitch, width, height);

// Device code
__global__ void MyKernel(float* devPtr,
                         size_t pitch, int width, int height)
{
    for (int r = 0; r < height; ++r) {
        float* row = (float*)((char*)devPtr + r * pitch);
        for (int c = 0; c < width; ++c) {
            float element = row[c];
        }
    }
}

3D:

// Host code
int width = 64, height = 64, depth = 64;
cudaExtent extent = make_cudaExtent(width * sizeof(float),
                                    height, depth);
cudaPitchedPtr devPitchedPtr;
cudaMalloc3D(&devPitchedPtr, extent);
MyKernel<<<100, 512>>>(devPitchedPtr, width, height, depth);

// Device code
__global__ void MyKernel(cudaPitchedPtr devPitchedPtr,
                         int width, int height, int depth)
{
    char* devPtr = devPitchedPtr.ptr;
    size_t pitch = devPitchedPtr.pitch;
    size_t slicePitch = pitch * height;
    for (int z = 0; z < depth; ++z) {
        char* slice = devPtr + z * slicePitch;
        for (int y = 0; y < height; ++y) {
            float* row = (float*)(slice + y * pitch);
            for (int x = 0; x < width; ++x) {
                float element = row[x];
            }
        }
    }
}

使用2D阵列可以很容易地看到音高的使用。他们将数组指针转换为char*的原因是,音高返回字节大小,而不是元素数量(音高可能不是元素大小的倍数)。

使用3D阵列,只需使用每个2D阵列的高度进行扩展即可。这类似于将3D结构展开到许多2D切片中。

答案 1 :(得分:0)

内存是一维连续的字节空间。 1D,2D和3D访问模式取决于您如何解释数据以及如何通过1D,2D和3D线程块访问它们。

  

cudaMallocPitch 至少分配设备上线性内存的宽度(以字节为单位)*高度字节。函数可以填充分配以确保任何给定行中的对应指针将继续满足合并的对齐要求,因为地址从行更新为行。 音高以*音调返回cudaMallocPitch()是分配的宽度(以字节为单位)。 - 内存管理   [CUDA Runtime API]。

M (rows) x N (cols) x K (slices)的数据的情况下,一个切片的每个像素将位于

i = blockIdx.x*BLOCK_X + threadIdx.x;
j = blockIdx.y*BLOCK_Y + threadIdx.y;

linearIdx = i + j*pitch/sizeof(float);

下一片像素是M x N个位置分开。因此,要访问您的数据,您必须正确跳转到下一个切片。就是这样,

i = blockIdx.x*BLOCK_X + threadIdx.x;
j = blockIdx.y*BLOCK_Y + threadIdx.y;
// index for the slice
k = blockIdx.z*BLOCK_Z + threadIdx.z;

// for the sake of simplicity 
int next_row_pitched = pitch/sizeof(float);

linearIdx = i + j*next_row_pitched + k*next_row_pitched*N;

要获得“第三个”维度,您必须“跳转”一个切片的所有像素,即M x N个位置。当每一行被调整时,您必须将M更改为cudaMallockPitch返回的音调值。

如果启动3D线程块,则上述索引有效。您还可以启动2D线程块,然后迭代切片数。

CUDA C编程指南的第3.2.2章“设备内存”有一个代码示例,它分配宽度×高度×深度的浮点值3D数组,并显示如何在设备代码中循环数组元素。