我目前正在使用Mathematica提供的CUDALink
包装器在我的GPU(GTX560Ti)上运行简单的3D模板转换。块尺寸对我来说并不重要,因为我没有使用任何共享内存或寻找优化(现在)。
因此,我可以为blockDim.x
和blockDim.y
设置任意合理的数字。无论我设置什么尺寸,包装器都会启动适当数量的块,没问题。但是,在z维度中,仅启动单个块。因此,blockDim.z
限制了我可以在该方向上计算的总点数。
为什么z方向只有一个块?我该如何解决这个问题?
供参考,这是我正在使用的内核:
__global__ void conv(Real_t in[48][48][48], Real_t out[48][48][48], mint stencil[13][13][13], mint length, mint rad) {
int x = threadIdx.x + blockIdx.x*blockDim.x;
int y = threadIdx.y + blockIdx.y*blockDim.y;
int z = threadIdx.z + blockIdx.z*blockDim.z;
while (x<length||y<length||z<length) {
out[x][y][z] = 0;
for (int ix = -rad; ix <= rad; ix++) {
for (int iy = -rad; iy <= rad; iy++) {
for (int iz = -rad; iz <= rad; iz++) {
if ( (fminf(x,fminf(y,z))-rad >= 0)
&& (fmaxf(x,fmaxf(y,z))+rad < length) )
{out[x][y][z] += stencil[ix+rad][iy+rad][iz+rad]*in[ix+x][iy+y][iz+z];}
} } }
if (x<length) {
x+= blockDim.x * gridDim.x;
} else if (y<length) {
y+= blockDim.y * gridDim.y;
} else if (z<length) {
z+= blockDim.z * gridDim.z;
}
}
}
请注意:变量length
对应于数组的维度(例如48)。 rad
与模板有关,小于length
。 stencil
只是一个0和1的数组,用于从in
中选择我想要总结为out
的内容。
我使用以下代码在Mathematica中运行内核:
Needs["CUDALink`"];
conv = CUDAFunctionLoad[code (*the kernel above, stored as a string*), "conv", {{_Real, _, "Input"}, {_Real, _, "Output"}, {_Integer , _, "Input"}, _Integer, _Integer}, {4, 4, 10}, "TargetPrecision" -> "Single", "XCompilerInstallation" -> "/usr/local/gcc44/bin/", "CleanIntermediate" -> False];
output = ConstantArray[1, {length, length, length}];
result = conv[input, output, stencil, length, rad];
为了说明我的问题,这里是我输出的一部分(显然我不能发布图像):
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0. 0.000578704 0.00173611 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.000289352 0.000868056 0.00173611 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.000578704 0.00144676 0.00260417 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.00115741 0.00202546 0.00347222 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.00115741 0.00202546 0.00347222 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.000578704 0.00144676 0.00289352 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.000578704 0.00144676 0.00289352 1. 1. 1.
这是blockDim.z = 10
生成的。零和分数是有用的值,但这些值只是我初始化out
数组的值。仅计算前10列,对应于z方向上的单个块。 (对于blockDim.z
和1
之间64
的任何值(费米GPU的上限),此行为都是可重现的。
答案 0 :(得分:1)
好吧,我猜这种行为只是CUDAResources中的一个错误而不是实际的编程问题。 (尽管如此,只有一个街区。我现在拥有的是一种解决方法。)
我使用CUDAResourcesUninstall[]
删除了CUDAResources,重新启动了Mathematica,使用CUDAResourcesInstall["/path/to/paclet/file",Update->True]
重新安装,然后重新启动了Mathematica。
然后我将内核更改为以下代码:
__global__ void conv(Real_t in[48][48][48], Real_t out[48][48][48], \
mint stencil[13][13][13], mint length, mint rad) {
int x = threadIdx.x + blockIdx.x*blockDim.x;
int y = threadIdx.y + blockIdx.y*blockDim.y;
int z = threadIdx.z + blockIdx.z*blockDim.z;
while (z<length) {
out[x][y][z] = 0;
for (int ix = -rad; ix <= rad; ix++) {
for (int iy = -rad; iy <= rad; iy++) {
for (int iz = -rad; iz <= rad; iz++) {
if ( (fminf(x,fminf(y,z))-rad >= 0)
&& (fmaxf(x,fmaxf(y,z))+rad < length) )
{out[x][y][z] += stencil[ix+rad][iy+rad][iz+rad]*in[ix+x][iy+y][iz+z];}
} } }
if (z<length) {
z+= blockDim.z * gridDim.z;
}
}
}
现在它有效。希望它保持这种状态。这当然意味着在z方向上进行的并行化较少,因为基本上有一个线程块在网格上顺序进行,而不是多个块并行工作。但是没关系,代码对我的目的来说足够快。
非常感谢所有帮助过的人。