奇怪的OpenCL调用C++上的副作用来提高循环性能
Weird OpenCL calls side effect on C++ for loop performance
我正在使用OpenCL进行一个C++项目。我使用CPU作为带有英特尔OpenCL运行时的OpenCL设备
我注意到调用OpenCL函数有一个奇怪的副作用。这里有一个简单的测试:
#include <iostream>
#include <cstdio>
#include <vector>
#include <CL/cl.hpp>
int main(int argc, char* argv[])
{
/*
cl_int status;
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
std::vector<cl::Device> devices;
platforms[1].getDevices(CL_DEVICE_TYPE_CPU, &devices);
cl::Context context(devices);
cl::CommandQueue queue = cl::CommandQueue(context, devices[0]);
status = queue.finish();
printf("Status: %dn", status);
*/
int ch;
int b = 0;
int sum = 0;
FILE* f1;
f1 = fopen(argv[1], "r");
while((ch = fgetc(f1)) != EOF)
{
sum += ch;
b++;
if(b % 1000000 == 0)
printf("Char %d readn", b);
}
printf("Sum: %dn", sum);
}
这是一个简单的循环,逐字符读取文件并添加它们,这样编译器就不会试图对其进行优化。
我的系统是运行Ubuntu 14.10的酷睿i7-4770K,2TB HDD 16GB DDR3。上面的程序以一个100MB的文件作为输入,大约需要770ms。这与我的硬盘速度一致。到目前为止还不错。
如果你现在反转注释,只运行OpenCL调用区域,大约需要200ms。再一次,到目前为止,一切都很好。
但是,如果取消所有注释,程序将花费超过2000毫秒的时间。我预计是770ms+200ms,但现在是2000ms。您甚至可以注意到for循环中输出消息之间的延迟增加了。这两个区域(OpenCL调用和读取字符)应该是独立的。
我不明白为什么使用OpenCL会干扰用于循环性能的简单C++。这不是一个简单的OpenCL初始化延迟。
我用编译这个例子
g++ weird.cpp -O2 -lOpenCL -o weird
我也尝试过使用Clang++,但它也是一样。
这是一个有趣的例子。这是因为在实例化队列时,getc是线程安全的版本,因此时间的增加是锁的抓取-释放周期-我不确定为什么/如何发生这种情况,但这是AMD OpenCL SDK与英特尔CPU的决定性点。我很惊讶,我的时间基本上和安大略省警局的时间一样
https://software.intel.com/en-us/forums/topic/337984
您可以尝试通过将getc更改为getc_unlocked来解决此特定问题。
对我来说,它将时间降到了930毫秒——超过750毫秒的时间增长主要用于平台和上下文创建行。
我认为这种影响是由于OpenCL对象仍在作用域中,因此在for循环之前没有被删除。由于需要考虑,它们可能会影响其他计算。例如,在我的系统上运行您给出的示例会产生以下时间(在Mac OSX上使用O2的g++4.2.1):
CL: 0.012s
Loop: 14.447s
Both: 14.874s
但是,将OpenCL代码放入其自己的匿名范围中,因此在循环之前自动调用析构函数似乎可以解决问题。使用代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include "cl.hpp"
int main(int argc, char* argv[])
{
{
cl_int status;
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
std::vector<cl::Device> devices;
platforms[1].getDevices(CL_DEVICE_TYPE_CPU, &devices);
cl::Context context(devices);
cl::CommandQueue queue = cl::CommandQueue(context, devices[0]);
status = queue.finish();
printf("Status: %dn", status);
}
int ch;
int b = 0;
int sum = 0;
FILE* f1;
f1 = fopen(argv[1], "r");
while((ch = fgetc(f1)) != EOF)
{
sum += ch;
b++;
if(b % 1000000 == 0)
printf("Char %d readn", b);
}
printf("Sum: %dn", sum);
}
我知道时间:
CL: 0.012s
Loop: 14.635s
Both: 14.648s
这似乎是线性增加的。与系统上的其他影响(例如其他进程的CPU负载)相比,这种影响非常小,但在添加匿名作用域时,这种影响似乎已经消失。我会做一些分析,如果它产生了任何感兴趣的东西,我会将其添加为编辑。
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- C++中循环的性能差异
- C++循环性能的倍数
- 在原始循环上使用boost::irange的性能损失
- OpenMP 嵌套循环处理性能
- 虚拟函数调用的性能作为 for 循环中的上限
- 在 C++ 中使用 OpenMP 并行化两个 for 循环不会提供更好的性能
- C++和Java的字符串循环性能比较
- C++:if 内部循环的性能影响
- 三个数字之间的 C++ 相加两个更高的数字,没有循环和数组
- 为什么在循环外举起弦会导致性能较慢
- 奇怪的OpenCL调用C++上的副作用来提高循环性能
- 三方快速排序需要更高的性能
- C# 与循环性能测量的 C++
- pthread_mutex锁是否提供比用户在代码中施加的内存屏障更高的性能
- 内部评估的布尔条件以获得循环性能
- 局部变量的循环性能
- 用于循环性能差异和编译器优化
- 如何在读取文件时获得更高的性能