为什么这个简单的OpenCL内核运行得如此之慢?

时间:2012-10-11 19:08:56

标签: performance opencl gpgpu

我正在研究OpenCL,我有点困惑为什么这个内核运行得如此之慢,与我期望它运行的方式相比。这是内核:

__kernel void copy(
  const __global char* pSrc, 
  __global __write_only char* pDst, 
  int length)
{
  const int tid = get_global_id(0);

  if(tid < length) {
    pDst[tid] = pSrc[tid];
  }
}

我已按以下方式创建缓冲区:

char* out = new char[2048*2048];
cl::Buffer(
  context,
  CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY,
  length,
  out);

同样适用于输入缓冲区,除了我已经将in指针初始化为随机值。最后,我以这种方式运行内核:

cl::Event event;
queue.enqueueNDRangeKernel(
  kernel, 
  cl::NullRange,
  cl::NDRange(length),
  cl::NDRange(1), 
  NULL, 
  &event);

event.wait();

平均而言,时间约为75毫秒,计算公式为:

cl_ulong startTime = event.getProfilingInfo<CL_PROFILING_COMMAND_START>();
cl_ulong endTime = event.getProfilingInfo<CL_PROFILING_COMMAND_END>();
std::cout << (endTime - startTime) * SECONDS_PER_NANO / SECONDS_PER_MILLI << "\n";

我正在使用英特尔i5-3450芯片(Sandy Bridge架构)运行Windows 7。为了比较,执行复制的“直接”方式花费不到5毫秒。我认为event.getProfilingInfo不包括主机和设备之间的通信时间。思考?

编辑:

根据ananthonline的建议,我将内核更改为使用float4s而不是chars,并将平均运行时间降低到大约50毫秒。仍然没有我希望的那么快,但是有所改进。谢谢ananthonline!

3 个答案:

答案 0 :(得分:3)

我认为您的主要问题是您正在使用的2048 * 2048个工作组。如果您拥有这么多单项工作组,系统上的opencl驱动程序必须管理更多的开销。如果您使用gpu执行此程序,这将特别糟糕,因为您将获得非常低的硬件饱和度。

优化:使用更大的工作组调用内核。您甚至不必更改现有内核。 see question: What should this size be?我以下面的64作为例子。在大多数硬件上,64恰好是一个不错的数字。

cl::size_t myOptimalGroupSize = 64;
cl::Event event;
queue.enqueueNDRangeKernel(
  kernel, 
  cl::NullRange,
  cl::NDRange(length),
  cl::NDRange(myOptimalGroupSize), 
  NULL, 
  &event);

event.wait();

除了复制单个值之外,您还应该让内核做更多的事情。我已经回答了关于全局记忆over here.

的类似问题

答案 1 :(得分:1)

CPU与GPU非常不同。在x86 CPU上运行,获得良好性能的最佳方法是使用double16(最大数据类型)而不是char或float4(如其他人所建议的那样)。

根据我在CPU上使用OpenCL的经验,我从未达到过OpenMP并行化所能达到的性能水平。 与CPU并行执行复制的最佳方法是将块分割成少量的大子块,并让每个线程复制一个子块。 GPU方法是正交的:每个线程参与同一块的副本。 这是因为在GPU上,不同的线程可以有效地访问连续的内存区域(合并)。

要使用OpenCL在CPU上执行高效复制,请在内核中使用循环来复制连续数据。然后使用不大于可用内核数量的工作组大小。

答案 2 :(得分:1)

我相信它是cl :: NDRange(1)告诉运行时使用单项工作组。这效率不高。在C API中,您可以为此传递NULL以使工作组大小保持在运行时;应该有一种方法在C ++ API中做到这一点(也许只是NULL)。这应该在CPU上更快;它肯定会在GPU上。