opencl最优组大小

opencl最优组大小,opencl,Opencl,我正在OpenCL上运行mandelbrot生成器(来自静态参数的2D图像)。 该计划非常简单: __kernel void mandelbrot(__global uchar * output, const float xstep, const float xoffset, const float ystep, const float yoffset,

我正在OpenCL上运行mandelbrot生成器(来自静态参数的2D图像)。 该计划非常简单:

__kernel
void mandelbrot(__global uchar * output, 
                const float xstep,
                const float xoffset,
                const float ystep,
                const float yoffset,
                const int maxiter)
{
    int gid_y = get_global_id(1);
    int gid_x = get_global_id(0);

    //calculate x and y on the fly for every pixel. 
    //This is just as fast as reading precalculated rulers from global memory.
    float x = gid_x * xstep + xoffset;
    float y = gid_y * ystep + yoffset;

    float real = 0;
    float imag = 0;

    int out = 0;

    for(int curiter = 0; curiter < maxiter; curiter++) {
        float nreal = real*real - imag*imag + x;
        imag = 2* real*imag + y;
        real = nreal;

        if (real*real + imag*imag > 4.0f) {
            out = curiter;
            break;
        }
    }

    //normalize output
    out *= 256.0 / (float)maxiter;
    output[gid_y * get_global_size(0) + gid_x] = out;
[/编辑]

我在我的Nvidia Quadro 1000M上运行它,它有2个计算单元和96个CUDA内核(每个计算单元48个内核)

我通过改变内核排队时的本地组大小来进行操作。这些是我在生成400Mpixel图像时获得的不同大小的性能结果。 所有数字都来自OpenCL探查器,不包括返回操作系统的最终内存拷贝。 图像是40992x10272-高度和宽度都可以被48整除

rows x columns
8x8: 397 MPixel/s
8x12: 505 MPixel/s
8x16: 523 MPixel/s
8x24: 521 MPixel/s
8x32: 520 MPixel/s
8x48: 520 MPixel/s

1x48: 321 MPixel/s
2x32: 424 MPixel/s
2x48: 523 MPixel/s
4x24: 519 MPixel/s
3x32: 525 MPixel/s
4x32: 525 MPixel/s
4x48: 525 MPixel/s

12x8: 490 MPixel/s
12x12:464 MPixel/s
12x24:505 MPixel/s
12x32:508 MPixel/s
12x48:433 MPixel/s

16x8: 499 MPixel/s
16x12:499 MPixel/s
16x16:472 MPixel/s
16x24:450 MPixel/s
16x32:440 MPixel/s
16x48:418 MPixel/s
其中一些数字让我感到困惑。 虽然很清楚为什么我能在48列中获得最佳结果(多亏了SIMD操作的工作原理),但我不明白:

  • 为什么在每组使用16行时性能会急剧下降
  • 为什么1x48的性能较差
  • 为什么我能用3x32、4x32和8x32获得最佳性能?!?我本以为有33%的SIMD处理器处于空闲状态,而实际上它看起来像是一个工作组坐在两个计算单元之间
  • 为什么首选工作组大小倍数返回32而不是48
  • 在任何GPU(ATI/Nvidia/Intel HD)上,只要我从OpenCL信息结构中获得什么,是否有一种非经验的方法来计算出最高性能的几何图形

  • 提前感谢

    这取决于您没有显示的代码。这是关键。 如果您的代码非常简单,即:
    out=8
    但是,正如您所说,引用的值\u WORK\u GROUP\u SIZE\u的倍数返回32。这意味着,32是计算单元在不影响性能的情况下可以并行启动的最大并发线程数。
    例如,启动32个以上的系统是没有意义的。如果使用32,您已经耗尽了本地内存存储,您需要重新使用全局内存(速度非常慢)

    如果您试图超过建议的限制,您将获得确切的性能下降。这并不是说32个更好,而是一个更好的选择。48是坏的

    我向你推荐:

  • 如果可能,请使用自动大小(将null作为本地大小传递给内核)。如果您不担心本地工作尺寸形状,这将实现最大性能
  • 如果需要手动设置本地大小,请使用引用的\u工作\u组\u大小\u倍数作为参考

  • 内核访问全局内存的方式至关重要,由工作组和全局维度决定:

    • 同一工作组中的连续工作项将写入哪些地址?这里的跨步是get_global_size(1),您可能希望交换X和Y。处理连续工作项中的连续元素通常更快。这是最重要的因素

    • 连续工作组将编写哪些地址?连续的工作组将经常在不同的计算单元上同时调度。他们最终可能会争夺同一渠道/银行,导致业绩下降

    • 通常最好写入32位整数而不是字节


    为了最大限度地提高性能,我建议您引入更多的按钮:在单个工作项中编写计算多个像素块(例如4x2)的内核,然后对(块大小)x(工作组大小)x(XY交换)x(图像大小)的所有组合进行基准测试。然后为你的GPU挑选最好的。

    在阅读以下内容之前,我回答了一个类似的问题,你可能会感兴趣

    为什么在每组使用16行时性能会急剧下降? 实际上,当您使用12行时,它已经退化了。内存访问按事务进行。事务将一次性获取一定数量的字节。现在,如果几个工作项试图访问数组中的几个连续元素,这意味着一个事务可能足以为它们提供全部服务

    因为您以这种方式访问内存:

    output[get_global_id(0)*get_global_size(1)+get_global_id(1)]=out

    这意味着维度0中的本地大小越大,事务的数量就越大,因为您必须访问非连续元素(由get_global_size(1)元素分隔)。而且全局内存访问非常昂贵

    因此,对于12/16行,至少需要12/16个事务。这引出了你的第二个问题:

    为什么1x48的性能较差? 根据我刚才所说的,性能应该是很好的,因为事务的数量应该是最小的

    但是现在出现了线程闲置的问题。正如其他人已经指出的,您得到的关于每个SM 48个内核的信息是错误的。线程在NVIDIA硬件上以32的组(称为NVIDIA的warp)执行。请注意,这些组称为波前,AMD最多可以有64个线程。因为在本例中,您有一个由48个线程(1乘48)组成的工作组,这意味着调度了64个线程。调度的线程数始终是32的倍数,因为您无法执行一部分扭曲

    因此,在本例中,有四分之一的线程不执行任何操作。实际上,当您与您获得的2x32(仍然是64个线程-2个扭曲,但已充分利用)的结果进行比较时,321 MPixel/s几乎是424 MPixel/s的3/4

    值得注意的是,这个结果:2x48:523 MPixel/s。在这种情况下,您的工作组大小是96乘以32。所以没有空闲线程

    为什么我能用3x32、4x32和8x32获得最佳性能?!? 答案来自前面的两个:使用32的倍数,并且保持维0中的线程数相对较小。但让我们更仔细地看看您的结果:

    2x32:  424 MPixel/s
    3x32:  525 MPixel/s
    4x32:  525 MPixel/s
    8x32:  520 MPixel/s
    16x32: 440 MPixel/s
    
    最后两行的性能下降很容易用所说的来解释。但是,
    2x32:  424 MPixel/s
    3x32:  525 MPixel/s
    4x32:  525 MPixel/s
    8x32:  520 MPixel/s
    16x32: 440 MPixel/s