我的OpenCL内核在更快的硬件上运行更慢..但为什么呢?

11
我在完成多核编程课程的项目时遇到了一些奇怪的问题,想和你们讨论一下。
我们被要求创建任何一个程序,以展示在多核平台上进行编程能够显著提高效率。我决定尝试使用OpenCL在GPU上编写一些代码。我选择了矩阵卷积问题,因为我对它非常熟悉(我之前使用open_mpi并行化过它,在处理大图像时速度有很大提升)。
于是这里是我的成果:我选择了一个大的GIF文件(2.5 MB)[2816X2112],然后运行了顺序版本(原始代码),得到了平均15.3秒的时间。
然后我运行了我刚写的新的OpenCL版本,使用我的MBP集成GeForce 9400M,我得到了1.26秒的平均计时。到目前为止都很好,速度提高了12倍!
但现在我去我的节能面板打开“图形性能模式”。该模式会关闭GeForce 9400M,并打开我的系统上拥有的Geforce 9600M GT。苹果表示,这张卡片的速度是集成卡的两倍。
猜猜看,我使用这张强大的显卡所需的时间是平均3.2秒……我的9600M GT似乎比9400M慢两倍以上。
对于那些熟悉OpenCL的人来说,我在开始之前将所有数据复制到远程缓冲区中,因此实际计算不需要往返主内存。另外,我让OpenCL确定最佳的本地工作大小,因为我读到他们在这个参数上有一个相当好的实现。
有人有什么线索吗?
编辑:完整的源代码和makefiles在这里http://www.mathieusavard.info/convolution.zip
cd gimage
make
cd ../clconvolute
make
put a large input.gif in clconvolute and run it to see results

更换显卡后,您是否重启了计算机?据我所知,这在这些计算机上是必需的。 - Georg Schölly
我已注销..当您想更改显卡时,它会强制您注销并重新登录+我的程序输出当前使用的显卡名称,以便我可以确定哪个正在运行.. - matdumsa
尝试重新启动...还尝试使用一个3264x2448像素大小、带有12X12掩码的图像来增加问题规模,只是发现结果相同... - matdumsa
5个回答

10
9400M是集成到内存控制器中的,而9600M GT是一张独立显卡,通过PCI-e总线连接到内存控制器。这意味着当您将内存传输到9400M时,它只会将其分配到系统RAM中。另一方面,9600M会将数据通过PCI-e发送到显卡上的专用图形内存。这种传输是导致您的基准测试似乎较慢的原因。
如果您想比较这两个显卡的性能,您应该使用OpenCL分析功能,而不是您当前正在使用的时钟功能。
clGetEventProfilingInfo(cl_event event, cl_profiling_info param_name, size_t param_value_size, void *param_value, size_t *param_value_size_ret)
将事件传递给此函数,该事件在排队内核时创建,并将CL_PROFILING_COMMAND_START作为第二个参数传递,以获取内核在纳秒级别的起始点,CL_PROFILING_COMMAND_END以获取内核的结束点。确保在内核执行完成后使用此命令(事件会保留其值,直到超出范围)。您还可以通过将此函数应用于缓冲区排队的事件来获取将数据传输到设备所需的时间。以下是一个示例:
        TRACE("Invoking the Kernel")
    cl::vector<cl::Event> matMultiplyEvent;
    cl::NDRange gIndex(32,64);
    cl::NDRange lIndex(16,16);

    err = queueList["GPU"]->enqueueNDRangeKernel(
                                                 matrixMultiplicationKernel, 
                                                 NULL, 
                                                 gIndex, 
                                                 lIndex, 
                                                 &bufferEvent,
                                                 matMultiplyEvent);
    checkErr(err, "Invoke Kernel");


    TRACE("Reading device data into array");
    err = queueList["GPU"]->enqueueReadBuffer(thirdBuff, 
                                              CL_TRUE,
                                              0,
                                              (matSize)*sizeof(float),
                                              testC,
                                              &matMultiplyEvent,
                                              bufferEvent);
    checkErr(err, "Read Buffer");
    matMultiplyEvent[0].wait();
    for (int i = 0; i < matSize; i++) {
        if (i%64 == 0) {
            std::cout << "\n";
        }
        std::cout << testC[i] << "\t";
    }
    long transferBackStart = bufferEvent[0].getProfilingInfo<CL_PROFILING_COMMAND_START>();
    long transferBackEnd = bufferEvent[0].getProfilingInfo<CL_PROFILING_COMMAND_END>();
    double transferBackSeconds = 1.0e-9 * (double)(transferBackEnd- transferBackStart);

    long matrixStart = matMultiplyEvent[0].getProfilingInfo<CL_PROFILING_COMMAND_START>();
    long matrixEnd = matMultiplyEvent[0].getProfilingInfo<CL_PROFILING_COMMAND_END>();
    double dSeconds = 1.0e-9 * (double)(matrixEnd - matrixStart);

这个例子使用了C++包装器,但概念应该是一样的。希望这可以帮到您。

谢谢,我很快会测试你所提出的东西,看看是否能够解释这个问题 :P - matdumsa

2
我得到了相同的结果,但我不确定原因。我的内核涉及非常少量的复制(我为所有内核调用提供了所有所需数据,并仅返回一个512x512的图像)。它是一个光线追踪器,因此内核工作远远超过复制回来的时间(从400毫秒到10毫秒)。尽管如此,9600M GT大约慢1.5倍至2倍。
根据nVidia的列表,9600M GT应该有32个SP(比9400M多两倍)。它的时钟速度也可能更高。
在某些情况下,例如游戏中,9600M GT似乎更快。请参见以下链接:http://www.videocardbenchmark.net/video_lookup.php?cpu=GeForce+9600M+GT http://www.videocardbenchmark.net/video_lookup.php?cpu=GeForce+9600M+GT 根据ars technica的报道:
此外,早期测试揭示了关于Snow Leopard实现的一个有趣细节。尽管Snow Leopard似乎没有为使用NVIDIA GeForce 9400M芯片组的机器启用双GPU或即时GPU切换(这是从Leopard继承下来的限制),但它似乎可以同时使用两者作为OpenCL资源。因此,即使您在MacBook Pro上启用了9600M GT,如果应用程序中遇到OpenCL代码,Snow Leopard也可以将该代码发送到9400M中几乎处于闲置状态的16个GPU核心进行处理。反之则不成立——当仅启用9400M运行MacBook Pro时,9600M GT会被完全关闭以节省电力,并且不能用作OpenCL资源。
这似乎与我们看到的相反。此外,我明确地一次只在一个设备上设置CL上下文。
ars论坛中有一些建议称9600M GT不太支持双精度,这可能解释了这个问题。我可能会尝试编写一个合成基准来测试这个假设。

1

当我在我的 MacBook 上测试 OpenCL 时,我遇到了同样的问题。我认为这是因为 GeForce 9400M 的总线速度比 Geforce 9600M GT 更高。因此,即使 Geforce 9600M GT 比 GeForce 9400M 更强大,将内存复制到 GPU 所需的时间太长,以至于无法看到更强大的 GPU 对您的情况的好处。这也可能是由于不合适的工作组大小引起的。

此外,我发现这个网站在我的 OpenCL 经验中非常有帮助。

http://www.macresearch.org/opencl


谢谢Kendall,但实际上我基于macresearch.org的东西编写了我的代码:P 工作组大小是通过传入空参数自动设置的。 - matdumsa
1
尝试使用不同的大小。默认值并不总是最好的选择。 - Kendall Hopkins
好的,我已经尝试了不同的大小..自动检测到的大小是16,在两张卡上都可以达到17,但会降低性能..在17以上会出现错误。奇怪,奇怪,奇怪... - matdumsa
1
对不起,我的意思是本地工作大小,而不是全局的。本地的大小应该在64-512左右。 - Kendall Hopkins

1
性能不是GeForce 9400M和Geforce 9600M GT之间唯一的差异。其中一个重大的差异在于前者是独立GPU,这带来了一系列的差异,其中以下几点可能会产生影响:
- 驱动程序倾向于批量处理更多指令 - 内存不均匀。GPU通常只访问自己的内存,而驱动程序则通过PCI-E总线来回移动内存。
我相信我还遗漏了一些...
以下是一些你可以尝试的想法:
- 避免调用clFinish。你在内存加载和执行之间调用它的方式强迫驱动程序做比必要更多的工作。它会使GPU停顿。 - 对代码进行性能分析以查看哪些部分占用了时间。我不知道是否支持CL性能分析,但是有了你的clFinish调用,它将通过简单地测量CPU端给你一个一阶估计。请注意,在一般情况下,很难区分由延迟造成的和由吞吐量造成的问题。

谢谢Bahbar,我尝试删除建议的cl_finish,但没有成功...然后我尝试删除它们所有(甚至是不安全的),但我仍然得到相同的运行时...有趣的是,如果我拔掉电脑的电源线让它在电池上运行,OpenCL运行时间会变长两倍(两个GeForce都是如此)。 - matdumsa

0

我对OpenCL还很陌生,所以可能有点天真,但我怀疑您不需要进入节能面板来切换OpenCL计算设备。我认为您可以在设置OpenCL上下文时选择设备。

我的假设是:1)如果您在未禁用集成GPU的情况下运行代码,则OpenCL会选择离散GPU作为计算设备。您的代码将在(快速的)离散GPU上运行。2)如果您首先禁用集成GPU,则会强制将运行OS X GUI的负载放到离散卡上。当您运行代码时,它将在离散GPU上运行,但会与GUI争夺资源。

这个答案是在11个月后回答的,但希望对某些人有用...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接