当我为一个多核编程课程完成我的项目编码时,我想到了一些非常奇怪的东西,我想与你讨论。
我们被要求创建任何程序,这些程序将显示出为多核平台编程的显着改进。我决定尝试在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
答案 0 :(得分:10)
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 ++包装器,但概念应该相同。
希望这有帮助。
答案 1 :(得分:2)
我得到了相同的结果,我不确定为什么。我的内核涉及非常小的复制(我预先为所有内核调用预先发送所有需要的数据,并且只返回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 forums中有一些建议认为9600M GT也不支持双打,这可以解释这个问题。我可能会尝试编写一个综合基准来测试这个假设。
答案 2 :(得分:1)
当我在MacBook上测试OpenCL时遇到了同样的问题。我相信这是因为GeForce 9400M的主存储器总线速度比Geforce 9600M GT高。因此,尽管Geforce 9600M GT的功率远远超过GeForce 9400M,但将内存复制到GPU所需的时间太长,无法看到更强大的GPU对您的情况的好处。这也可能是由于工人群体规模不合适造成的。
此外,我发现此网站对我的OpenCL体验非常有帮助。
答案 3 :(得分:1)
性能不是GeForce 9400M和Geforce 9600M GT的唯一区别。一个很重要的是,一个是离散 GPU。随之而来的是一系列差异,其中以下内容会产生影响:
我确定我错过了一些......
以下是您可以尝试的一系列想法:
答案 4 :(得分:0)
我是OpenCL的新手,所以我可能有点天真,但我怀疑你需要进入节能面板来切换OpenCL计算设备。我相信您在代码中设置OpenCL上下文时会选择设备。
我的假设:1)当您在不首先禁用集成GPU的情况下运行代码时,OpenCL会选择您的独立GPU作为计算设备。您的代码在(快速)离散GPU上运行。 2)首先禁用集成GPU时,会强制将OS X GUI运行到分立卡上。当您运行代码时,它在独立GPU上运行,但它会与您的GUI竞争资源。
问题被提出后11个月,这个答案即将到来,但希望这对某人有用......