我正在研究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!
答案 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上。