我一直在使用C#+ Cudafy( C# - > CUDA或OpenCL翻译器)的波模拟器工作得很好,除了运行OpenCL CPU版本(英特尔驱动程序, 15英寸MacBook Pro Retina i7 2.7GHz,GeForce 650M(Kepler,384核心))的速度大约是GPU版本的四倍。
(无论是使用CL还是CUDA GPU后端,都会发生这种情况.OpenCL GPU和CUDA版本的执行几乎相同。)
澄清一个样本问题:
我无法解释为什么CPU版本比GPU更快 。在这种情况下,在CPU和GPU上执行(在CL情况下)的内核代码是相同的。我在初始化期间选择CPU或GPU设备,但除此之外,一切都是相同的。
修改
这是启动其中一个内核的C#代码。 (其他人非常相似。)
public override void UpdateEz(Source source, float Time, float ca, float cb)
{
var blockSize = new dim3(1);
var gridSize = new dim3(_gpuEz.Field.GetLength(0),_gpuEz.Field.GetLength(1));
Gpu.Launch(gridSize, blockSize)
.CudaUpdateEz(
Time
, ca
, cb
, source.Position.X
, source.Position.Y
, source.Value
, _gpuHx.Field
, _gpuHy.Field
, _gpuEz.Field
);
}
而且,这是Cudafy生成的相关CUDA内核函数:
extern "C" __global__ void CudaUpdateEz(float time, float ca, float cb, int sourceX, int sourceY, float sourceValue, float* hx, int hxLen0, int hxLen1, float* hy, int hyLen0, int hyLen1, float* ez, int ezLen0, int ezLen1)
{
int x = blockIdx.x;
int y = blockIdx.y;
if (x > 0 && x < ezLen0 - 1 && y > 0 && y < ezLen1 - 1)
{
ez[(x) * ezLen1 + ( y)] = ca * ez[(x) * ezLen1 + ( y)] + cb * (hy[(x) * hyLen1 + ( y)] - hy[(x - 1) * hyLen1 + ( y)]) - cb * (hx[(x) * hxLen1 + ( y)] - hx[(x) * hxLen1 + ( y - 1)]);
}
if (x == sourceX && y == sourceY)
{
ez[(x) * ezLen1 + ( y)] += sourceValue;
}
}
为了完整起见,这里是用于生成CUDA的C#:
[Cudafy]
public static void CudaUpdateEz(
GThread thread
, float time
, float ca
, float cb
, int sourceX
, int sourceY
, float sourceValue
, float[,] hx
, float[,] hy
, float[,] ez
)
{
var i = thread.blockIdx.x;
var j = thread.blockIdx.y;
if (i > 0 && i < ez.GetLength(0) - 1 && j > 0 && j < ez.GetLength(1) - 1)
ez[i, j] =
ca * ez[i, j]
+
cb * (hy[i, j] - hy[i - 1, j])
-
cb * (hx[i, j] - hx[i, j - 1])
;
if (i == sourceX && j == sourceY)
ez[i, j] += sourceValue;
}
显然,这个内核中的if
很糟糕,但即使是最终的管道停顿也不应该导致这样的极端性能增量。
我跳出来的另一件事是我正在使用一个蹩脚的网格/块分配方案 - 即,网格是要更新的数组的大小,每个块是一个线程。我确信这会对性能产生一些影响,但我看不出它会导致它在CPU上运行的CL代码速度的1/4。哎呀!
答案 0 :(得分:8)
回答这个问题,将其从未答复的清单中删除。
发布的代码表明内核启动指定了1(活动)线程的线程块。这不是编写快速GPU代码的方法,因为它会使大部分GPU功能空闲。
典型的线程块大小应该至少为每个块128个线程,并且通常更高,为32的倍数,最高为每块512或1024的限制,具体取决于GPU。
GPU&#34;喜欢&#34;通过大量并行工作来隐藏延迟&#34;可用&#34;。为每个块指定更多线程有助于实现此目标。 (在网格中有相当多的线程块也可能有所帮助。)
此外,GPU以32个为一组执行线程。指定每个块只有1个线程或32个非倍数将在每个执行的线程块中留下一些空闲执行槽。每个块1个线程特别糟糕。