opencl最优组大小
我正在OpenCL上运行mandelbrot生成器(来自静态参数的2D图像)。 该计划非常简单:opencl最优组大小,opencl,Opencl,我正在OpenCL上运行mandelbrot生成器(来自静态参数的2D图像)。 该计划非常简单: __kernel void mandelbrot(__global uchar * output, const float xstep, const float xoffset, const float ystep, const float yoffset,
__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操作的工作原理),但我不明白:
提前感谢这取决于您没有显示的代码。这是关键。 如果您的代码非常简单,即:
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