【问题标题】:Cuda/cudafy 3d indexingCuda/cudafy 3d 索引
【发布时间】:2017-10-13 07:16:32
【问题描述】:

试图了解 cuda,在没有掌握类似的 stackoverflow 问题后,我决定测试一个示例(我在 c# 中使用 cudafy.net,但底层 cuda 应该是可解析的)

我想做以下事情。向内核发送一个 4x4x4 的矩阵,按照这个逻辑得到一个 4x4x4 的输出:

if(input[x,y,z] == 1)
    output[x+1, y, z]++;

if(input[x,y,z] == 2)
    output[x-1, y, z]++;

我研究了以下 cudafy 示例。

public const int N = 1 * 1024;

//Omissions

gpu.Launch(128, 1, function, dev_a, dev_b, dev_c);

内核:

[Cudafy]
public static void add_0(GThread thread, int[] a, int[] b, int[] c)
{
    int tid = thread.blockIdx.x; // (tid 0 -> 127, from my understanding)
    while (tid < N)
    { 
        c[tid] = a[tid] + b[tid];
        tid += thread.gridDim.x;
    }
}

然后尝试将其转移到 3d。我无法正确建立索引。说我有以下。 (这里三个数组只是为了测试索引)

int size = 4;
int[] dev_delta = gpu.Allocate<int>(size * size * size);
int[] dev_space = gpu.Allocate<int>(size * size * size);
int[] dev_result = gpu.Allocate<int>(size * size * size);

gpu.Launch(new dim3(4, 4, 4), 1, "testIndex", dev_delta, dev_space, dev_result);

还有内核:

[Cudafy]
public static void testIndex(GThread thread, int[] delta, int[] space, int[] result)
{
    int x = thread.blockIdx.x;
    int y = thread.blockIdx.y;
    int z = thread.blockIdx.z;
    delta[x]++;
    space[y]++;
    result[z]++;
}

我天真地期待以下内容:

delta = {4,4,4,4,0,0,0,0,0, ... 0,0}
space = {4,4,4,4,0,0,0,0,0, ... 0,0}
result = {4,4,4,4,0,0,0,0,0 ... 0,0}

但我明白了:

delta = {1,1,1,1,0,0,0,0,0, ... 0,0}
space = {1,1,1,1,0,0,0,0,0, ... 0,0}
result = {1,0,0,0,0,0,0,0,0 ... 0,0}

这对我来说毫无意义,显然我错过了一些东西。

问题:

我启动了多少线程?

如何在 3 维中“索引”我的示例问题(启动 4x4x4 线程并获取 flat3DArray[x * sizeY * sizeZ + y * sizeZ + z] 的变量)?

您如何在二维中“索引”我的示例问题? (启动 4x4 线程,然后让每个线程处理长度为 4 的深度列)

我发现这可能与 Why is z always zero in CUDA kernel 相关,如果这让我感到困惑,我仍然会很感激纯 cuda 的答案来整理我的大脑

【问题讨论】:

    标签: c# cuda gpgpu cudafy.net


    【解决方案1】:

    我要启动多少线程? 每个块启动 1 个线程,因此总共 16 个线程,因为没有使用 Z 参数。为了获得更好的性能,我建议还使用线程(至少 128 个,并且无论如何都是 32 的倍数)。

    如何在 3 维中“索引”我的示例问题(启动 4x4x4 线程并获取 flat3DArray[x * sizeY * sizeZ + y * sizeZ + z] 的变量)? gpu.Launch 方法的第二个参数用于线程。 xyz 因此可以分别为 threadIdx.xthreadIdx.ythreadIdx.z。但是您可能还想使用许多块,因此threadIdx.x + blockDim.x * blockIdx.x 可能是一个很好的峰值。

    您提供的链接here 解释了为什么您的 Z 尺寸不相关。 CUDAfy.Net 公开了进一步调用 cuda 运行时 CUDA/C API 调用的启动函数。当将参数从 dot net 传递到本机环境时,CUDAfy.Net 似乎只是忽略了 Z 参数,将其保留为 1。 (这很可能是由于早期版本的 CUDA 不支持不同于 1 的 Z 参数)。 解释不是纯 cuda,因为 CUDA 现在支持不同于 1 的 Z 值,但您的参数在 CUDAfy.Net 实现中被简单地忽略了。

    【讨论】:

    • 但是16个线程并不能解释我的结果。我假设每个线程都运行我的内核,然后:delta[x]++; 应该运行 16 次,并且该数组中元素的总和应该等于 16。我做错了什么,是 cudafy 坏了还是我错过了一些关于内存管理的事情设备?我的 delta[x]++;可能会互相覆盖?
    • @Adam,由于增量既不是原子的,也不是针对数据竞争问题的保护,很可能所有线程读取零递增寄存器中的值并将一写入输出。请注意,每个块有一个线程,几个块可以同时并行运行。此外,读/写的延迟使得这种模式很可能发生。
    猜你喜欢
    • 2013-01-15
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    相关资源
    最近更新 更多