我的OpenCL内核在更快的硬件上速度较慢。。但是为什么呢?

我的OpenCL内核在更快的硬件上速度较慢。。但是为什么呢?,opencl,hardware-acceleration,Opencl,Hardware Acceleration,当我为一个多核编程类完成我的项目编码时,我想到了一件非常奇怪的事情,我想和你们讨论一下 我们被要求创建任何一个程序,该程序将在为多核平台编程时显示出显著的改进。我决定尝试在GPU上编写一些代码来试用OpenCL。我之所以选择矩阵卷积问题,是因为我对它非常熟悉(以前我用open_mpi对它进行了并行处理,对于大型图像具有很高的加速比) 就是这样,我选择了一个大的GIF文件(2.5 MB)[2816X2112],然后运行顺序版本(原始代码),平均得到15.3秒 然后我运行我刚刚在我的MBP集成GeF

当我为一个多核编程类完成我的项目编码时,我想到了一件非常奇怪的事情,我想和你们讨论一下

我们被要求创建任何一个程序,该程序将在为多核平台编程时显示出显著的改进。我决定尝试在GPU上编写一些代码来试用OpenCL。我之所以选择矩阵卷积问题,是因为我对它非常熟悉(以前我用open_mpi对它进行了并行处理,对于大型图像具有很高的加速比)

就是这样,我选择了一个大的GIF文件(2.5 MB)[2816X2112],然后运行顺序版本(原始代码),平均得到15.3秒

然后我运行我刚刚在我的MBP集成GeForce 9400M上编写的新OpenCL版本,平均计时1.26秒。。到目前为止还不错,速度提高了12倍

但现在我进入我的节能面板,打开“图形性能模式”,该模式关闭GeForce 9400M,打开我的系统拥有的GeForce 9600M GT。苹果公司表示,这种卡的速度是集成卡的两倍

你猜怎么着,我使用最棒的图形卡计时平均为3.2秒…我的9600M GT似乎比9400M慢两倍多

对于那些倾向于OpenCL的人,我会在开始之前将所有数据复制到远程缓冲区,因此实际计算不需要往返到主ram。另外,我让OpenCL确定最佳的本地工作大小,因为我读到他们在计算参数方面做了相当好的实现

有人有线索吗

编辑:此处包含makefile的完整源代码


我在MacBook上测试OpenCL时遇到了同样的问题。我相信这是因为GeForce 9400M比GeForce 9600M GT具有更高的主内存库总线速度。因此,即使Geforce 9600M GT的功率比Geforce 9400M大得多,将内存复制到GPU所需的时间也太长,无法看到更强大的GPU在您的情况下带来的好处。这也可能是由不适当的工人群体规模造成的

我还发现这个网站对我的OpenCL体验很有帮助


性能并不是GeForce 9400M和GeForce 9600M燃气轮机之间的唯一区别。一个大的是,这是一个离散的GPU。随之而来的是一系列的差异,其中有以下几点可能会产生影响:

  • 驱动程序批处理更多命令的趋势
  • 记忆是不统一的。GPU通常只访问自己的内存,驱动程序通过PCI-E总线来回移动内存
我肯定我错过了一些

以下是一些您可以尝试的想法:

  • 避免调用clFinish。您在内存加载和执行之间调用它的方式迫使驱动程序执行超出必要的工作。它会使GPU停止工作
  • 分析您的代码以查看需要花费的时间。我还不知道对CL性能分析的支持,但是对于您的clFinish调用,它通过简单地测量CPU端,为您提供了一个一阶估计。请注意,通常很难区分延迟和吞吐量

    • 我得到了同样的结果,但我不确定为什么。我的内核涉及到非常小的到/从复制(我为所有内核调用提供所有需要的数据,并且只返回一个512x512映像)。它是一个光线跟踪器,因此内核的工作大大超过了复制(400毫秒到10毫秒)。尽管如此,9600M燃气轮机的速度还是慢了1.5-2倍

      根据nVidia的列表,9600M GT应该有32个SP(是9400M的两倍)。它大概也更高了

      在某些情况下,9600M GT看起来确实更快,例如游戏。请参阅以下链接:

      根据:

      此外,关于雪豹实现的一个有趣的小道消息是通过早期测试揭示的。虽然Snow Leopard似乎不能启用双GPU或使用英伟达GeFiel9400米芯片的机器上的GPU切换,但从豹中所进行的限制看来,OS可以同时使用OpenCL资源。因此,即使在MacBook Pro上启用了9600M GT,如果在应用程序中遇到OpenCL代码,Snow Leopard也可以将该代码发送给9400M中处于休眠状态的16个GPU内核进行处理。相反的情况并非如此,但当运行仅启用9400M的MacBook Pro时,9600M GT会完全关闭以节省电源,并且不能用作OpenCL资源

      这似乎与我们所看到的相反。此外,我一次只在一台设备上显式设置CL上下文


      有一些建议,在9600M燃气轮机不支持双打以及,这将解释这个问题。我可能会尝试编写一个综合基准测试来验证这一假设。

      9400M集成到内存控制器,而9600M GT是一个离散卡,通过PCI-e总线连接到内存控制器。这意味着,当您将内存传输到9400M时,它只是将内存分配到系统RAM中。另一方面,9600M通过PCI-e将数据发送到卡上的专用图形内存。这种转移使您的基准测试看起来更慢

      如果您想比较两个图形卡的性能,您应该使用OpenCL评测函数,而不是当前使用的时钟函数

      cl_int clGetEventProfilingInfo(cl_事件事件,cl_分析信息参数名称,
      大小参数值大小、无效*参数值、大小参数值大小)

      将内核排队时创建的事件传递给函数,并将第二个参数的CL_PROFILING_命令\u START传递给函数,以纳秒为单位获取内核的起点,将CL_PROFILING_命令\u END传递给函数,以获取内核的终点。确保在内核执行完成后使用此命令(
      cd gimage
      make
      cd ../clconvolute
      make
      put a large input.gif in clconvolute and run it to see results
      
              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);