当我完成为多核编程课程编写项目时,我想到了一些非常奇怪的东西,我想和你讨论。
我们被要求创建任何程序,以显示为多核平台编程的显着改进。我决定尝试在GPU上编写代码以试用OpenCL。我已经选择了矩阵卷积问题,因为我对它非常熟悉(我之前使用open_mpi对它进行了并行化,对于大型图像具有很高的加速性能)。
所以在这里,我选择一个大的GIF文件(2.5 MB)[2816X2112],我运行顺序版本(原始代码),我得到平均15.3秒。
然后我运行我刚刚在我的MBP集成GeForce 9400M上编写的新OpenCL版本,我得到的平均时间为1.26秒。到目前为止一切都很好,它的速度是12倍!
但现在我进入节能面板打开“图形性能模式”该模式关闭GeForce 9400M并打开我的系统所具有的Geforce 9600M GT。 Apple称这张卡的速度是集成卡的两倍。
猜猜看,我使用kick-ass显卡的时间平均为3.2秒...我的9600M GT似乎比9400M慢两倍多..
对于那些倾向于OpenCL的人,我在启动之前将所有数据复制到远程缓冲区,因此实际计算不需要往返于主ram的往返。另外,我让OpenCL确定最佳本地工作量,因为我已经读过他们在确定参数时已经做了很好的实现。
有人有线索吗?
编辑:这里有makefile的完整源代码 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
9400M集成到您的内存控制器,而9600M GT是一个独立的卡,通过PCI-e总线连接到您的内存控制器。这意味着当您将内存传输到9400M时,它只需将其分配到系统RAM中。另一方面,9600M通过PCI-e将数据发送到卡上的专用图形内存。这种转移使您的基准显得更慢。
如果您想比较两个显卡的性能,您应该使用OpenCL分析功能,而不是您当前使用的时钟功能。
cl_int 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 ++包装器,但概念应该相同。
希望这可以帮助。
我得到了相同的结果,我不确定为什么。我的内核涉及非常小的复制(我预先为所有内核调用预先发送所有需要的数据,并且只返回512x512图像)。它是一个光线跟踪器,因此内核的工作量远远超过了复制(400 + ms到10ms)。尽管如此,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也不支持双打,这可以解释这个问题。我可能会尝试编写一个综合基准来测试这个假设。
我在MacBook上测试OpenCL时遇到了同样的问题。我相信这是因为GeForce 9400M的主存储器总线速度比Geforce 9600M GT高。因此,尽管Geforce 9600M GT的功率远远超过GeForce 9400M,但将内存复制到GPU所需的时间太长,无法在您的情况下看到更强大GPU的优势。它也可能是由不适当的工人组大小引起的。
此外,我发现这个网站对我的OpenCL体验很有帮助。
http://www.macresearch.org/opencl
性能不是GeForce 9400M和Geforce 9600M GT的唯一区别。一个重要的是一个是一个 离散的 GPU。随之而来的是一系列差异,其中以下内容会产生影响:
- 司机批评更多命令的倾向
- 记忆不统一。 GPU通常只访问自己的内存,驱动程序通过PCI-E总线来回移动内存。
我确定我错过了一些......
以下是您可以尝试的一些想法:
- 避免调用clFinish。在内存加载和执行之间调用它的方式会强制驱动程序执行超出必要的工作。它阻止了GPU。
- 配置您的代码以查看 什么 正在花时间。我还没有意识到对CL性能分析的支持,但是通过clFinish调用,它可以通过简单地测量CPU端来为您提供一阶估计。请注意,通常很难区分延迟和吞吐量导致的问题。
我是OpenCL的新手,所以我可能有点天真,但我怀疑你需要进入节能面板来切换OpenCL计算设备。我相信您在代码中设置OpenCL上下文时选择了设备。
我的假设:1)当您在不首先禁用集成GPU的情况下运行代码时,OpenCL会选择您的独立GPU作为计算设备。您的代码在(快速)离散GPU上运行。 2)当您首先禁用集成GPU时,会强制将OS X GUI运行到您的离散卡上。当您运行代码时,它在独立GPU上运行,但它会与您的GUI争用资源。
问题被提出后11个月就会出现这个答案,但希望这对某些人有用......