简单pycuda内核的奇怪行为

Strange behaviour of simple pycuda kernel

我对 cuda 和 pycuda 很陌生。 我需要一个内核,通过简单地 "repeating" 相同的数组 n 次,从数组 (1 x d) 中创建一个矩阵(维度为 n x d): 例如,假设我们有 n = 4 和 d = 3,那么如果数组是 [1 2 3] 我的内核的结果应该是:

[1 2 3
 1 2 3
 1 2 3
 1 2 3]

(矩阵 4x3)。

基本上,它与 numpy.tile(array, (n, 1))

相同

我写了下面的代码:

kernel_code_template = """
__global__ void TileKernel(float *in, float *out)
{
    // Each thread computes one element of out
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    int x = blockIdx.x * blockDim.x + threadIdx.x;

    if (y > %(n)s || x > %(d)s) return;

    out[y * %(d)s + x] = in[x];
}
"""

d = 64
n = 512

blockSizex = 16
blockSizey = 16
gridSizex = (d + blockSizex - 1) / blockSizex 
gridSizey = (n + blockSizey - 1) / blockSizey 

# get the kernel code from the template 
kernel_code = kernel_code_template % {
    'd': d,
    'n': n
    }
mod = SourceModule(kernel_code)
TileKernel = mod.get_function("TileKernel")

vec_cpu = np.arange(d).astype(np.float32) # just as an example
vec_gpu = gpuarray.to_gpu(vec_cpu)
out_gpu = gpuarray.empty((n, d), np.float32)

TileKernel.prepare("PP")
TileKernel.prepared_call((gridSizex, gridSizey), (blockSizex, blockSizey, 1), vec_gpu.gpudata,  out_gpu.gpudata)

out_cpu = out_gpu.get()

现在,如果我 运行 这段带有 d 的代码等于 2 >= 16 的幂,我会得到正确的结果(就像 numpy.tile(vec_cpu, (n, 1 ))); 但是如果我将 d 设置为其他任何值(比如 88),我得到输出矩阵的每个元素都有 正确值,除了第一列:一些条目是正确的,但其他条目有另一个值,显然是随机的,每个错误元素都相同,但每个 运行 不同, 并且第一列中具有错误值的条目每个 运行 都不相同。 示例:

[0  1  2
 0  1  2
 6  1  2
 0  1  2
 6  1  2
 ...]

我真的不知道是什么导致了这个问题,但也许这只是我遗漏的一些简单的事情...

任何帮助将不胜感激,提前致谢!

您的内核代码中的边界检查不正确。这个

if (y > n ||  x > d) return;
out[y * d + x] = in[x];

应该是:

if (y >= n ||  x >= d) return;
out[y * d + x] = in[x];

或更好:

if ((y < n) && (x < d))
    out[y * d + x] = in[x];

数组中的所有数组有效索引位于 0 < x < d0 < y < n 上。通过允许 x=d 你有未定义的行为,允许输出数组下一行中的第一个条目被未知值覆盖。这就解释了为什么有时结果是正确的,而有时则不是。