C# Cuda-OpenCL CPU比OpenCL或Cuda GPU版本快4倍

C# Cuda-OpenCL CPU比OpenCL或Cuda GPU版本快4倍,c#,cuda,opencl,cudafy.net,C#,Cuda,Opencl,Cudafy.net,我用C#+Cudafy(C#->CUDA或OpenCL翻译程序)开发的波形模拟器工作得很好,除了运行OpenCL CPU版本(英特尔驱动程序,15英寸MacBook Pro Retina i7 2.7GHz,GeForce 650M(开普勒,384核))的速度大约是GPU版本的四倍 (无论我使用CL还是CUDA GPU后端,都会发生这种情况。OpenCL GPU和CUDA版本的性能几乎相同。) 要澄清,对于示例问题: OpenCLCPU 1200赫兹 OpenCL GPU 320 Hz CUD

我用C#+Cudafy(C#->CUDA或OpenCL翻译程序)开发的波形模拟器工作得很好,除了运行OpenCL CPU版本(英特尔驱动程序,15英寸MacBook Pro Retina i7 2.7GHz,GeForce 650M(开普勒,384核))的速度大约是GPU版本的四倍

(无论我使用CL还是CUDA GPU后端,都会发生这种情况。OpenCL GPU和CUDA版本的性能几乎相同。)

要澄清,对于示例问题:

  • OpenCLCPU 1200赫兹
  • OpenCL GPU 320 Hz
  • CUDA GPU-~330 Hz
我无法解释为什么CPU版本会比GPU快。在这种情况下,在CPU和GPU上执行的内核代码(在CL情况下)是相同的。我在初始化期间选择CPU或GPU设备,但除此之外,一切都是相同的

编辑

下面是启动其中一个内核的C#代码(其他内核非常相似)

以下是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;
    }
}
extern“C”\uuuuuu global\uuuuuuuuuuvoid cudaupdatez(浮点时间、浮点ca、浮点cb、浮点源X、浮点源Y、浮点源值、浮点*hx、浮点hxLen0、浮点hxLen1、浮点*hyLen0、浮点*ez、浮点Exlen0、浮点Exlen1)
{
int x=blockIdx.x;
int y=块idx.y;
如果(x>0&&x0&&y
为了完整起见,这里是用于生成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;
    }
[Cudafy]
公共静态无效CUDAUPDATEZ(
GThread螺纹
,浮动时间
,浮子约
,浮动cb
,int sourceX
,int sourceY
,浮点源值
,浮动[,]hx
,float[,]hy
,float[,]ez
)
{
var i=thread.blockIdx.x;
var j=thread.blockIdx.y;
如果(i>0&&i0&&j
显然,这个内核中的
if
是不好的,但即使是由此产生的管道暂停也不应该导致如此极端的性能增量


另一件让我吃惊的事情是,我使用的是一个蹩脚的网格/块分配方案——即网格是要更新的数组的大小,每个块都是一个线程。我确信这对性能有一定影响,但我看不到这会导致它的速度是CPU上运行CL代码速度的1/4。啊!

回答这个问题把它从没有答复的名单上去掉

发布的代码表明内核启动指定了一个由1个(活动)线程组成的线程块。这不是编写快速GPU代码的方法,因为它会使大部分GPU功能闲置

典型的threadblock大小应至少为每个块128个线程,更高的通常更好,以32的倍数表示,最多为每个块512或1024个线程,具体取决于GPU

GPU“喜欢”通过大量的并行工作来隐藏延迟。为每个块指定更多的线程有助于实现这一目标。(在网格中有合理数量的线程块也可能有帮助。)


此外,GPU以32个线程为一组执行线程。在每个执行的线程块中,只指定每个块1个线程或不指定32的倍数将留下一些空闲的执行槽。每个块1个线程特别糟糕。

您有一些可以共享的代码示例吗?@EricBainville当然-您想要C#、CUDA或CL内核吗,或者什么?(这是一个半中型应用程序。我不想在其中粘贴20k行代码)我看不到任何迹象表明cuda内核每个块使用的线程数超过1个(没有使用
threadIdx.x
threadIdx.y
)。此外,此次发布指定每个块1个线程。这意味着大约97%的GPU功能未使用。我对cudafy了解不多,所以我不知道你是否能控制这一点,但我对cuda代码运行速度不快一点也不感到惊讶。在发布有关性能的问题时,请发布一个reprod使用gridDim、blockDim和所有循环的执行次数对源代码进行可复制或注释。启动1个线程的块不太可能允许CPU实现对代码进行矢量化。在NVIDIA GPU上,您将以远低于1/32的计算效率执行,在AMD GPU上,您将以低于1/64的计算效率执行cy.我建议您分析GPU代码。@HansRudel NVIDIA GPU请参阅《CUDA C编程指南》,以了解有关SIMT体系结构和执行模型的详细信息。在计算能力1.0-3.5设备上,WARP_大小为32个线程。指定一个带1个线程的块将导致硬件执行1个带1个活动线程和31个非活动线程的WARPeads的效率为3%。AMD GPU以64个线程(称为Wavefront)为一组来管理和执行指令。
    [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;
    }