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

Performance 为什么这个简单的OpenCL内核运行得这么慢?,performance,opencl,gpgpu,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]; } } 输入

我正在研究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];
  }
}
输入缓冲区也是如此,只是我已经初始化了指向随机值的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";
cl_ulong startTime=event.getProfilingInfo();
cl_ulong endTime=event.getProfilingInfo();

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

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

event.wait();
优化:使用较大的工作组调用内核。您甚至不必更改现有的内核。我以下面的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();

您还应该让内核做的不仅仅是复制单个值。我已经回答了一个关于全局内存的类似问题,CPU与GPU非常不同。在x86CPU上运行此功能,获得良好性能的最佳方法是使用double16(最大的数据类型)而不是char或float4(如其他人所建议的)

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


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

我相信是cl::NDRange(1)告诉运行时使用单个项工作组。这是没有效率的。在C API中,您可以为此传递NULL,使工作组的大小取决于运行时;在C++ API中也应该有这样的方法(也许也只是null)。这在CPU上应该更快;它肯定会在GPU上。

这是什么实现?英特尔OpenCL的实现?您是否尝试过使用4浮点数组的同一内核?这可能是一种更好的内存访问模式。是的,英特尔OpenCL实现。我还没试过4号浮球,这是个好主意。我来调查一下。出于好奇,
clEnqueueCopyBuffer
的性能如何?这就是你所说的“直接”吗?@willglynn好多了。分析信息报告的时间约为2毫秒。在QueryPerformanceCounters中包装对event.wait()的调用会报告更高的时间,大约8毫秒,但这当然包括等待调用本身的开销。@willglynn但我所说的“direct”是一个memcopy调用。我知道,这有点像苹果对桔子,但它至少提供了某种基线。