奇怪的 OpenCL 调用 C++ 的副作用以提高循环性能
Weird OpenCL calls side effect on C++ for loop performance
我正在使用 OpenCL 开发一个 C++ 项目。我将 CPU 用作带有 intel OpenCL runtime
的 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: %d\n", 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 read\n", b);
}
printf("Sum: %d\n", sum);
}
这是一个简单的循环,一个字符一个字符地读取文件并将它们相加,这样编译器就不会尝试优化它。
我的系统是 Core i7-4770K,2TB HDD 16GB DDR3 运行ning Ubuntu 14.10。上面的程序以一个 100MB 的文件作为输入,大约需要 770 毫秒。这与我的硬盘速度一致。到目前为止一切顺利。
如果您现在反转注释并且 运行 只有 OpenCL 调用区域,则大约需要 200 毫秒。再一次,到目前为止,一切都很好。
Buf如果全部取消注释,程序耗时超过2000ms。我期望 770ms + 200ms,但它是 2000ms。您甚至可以注意到 for 循环中输出消息之间的延迟增加。这两个区域(OpenCL 调用和读取字符)应该是独立的。
我不明白为什么使用 OpenCL 会干扰简单的 C++ for 循环性能。这不是简单的 OpenCL 初始化延迟。
我正在编译这个例子:
g++ weird.cpp -O2 -lOpenCL -o weird
我也尝试过使用 Clang++,但结果是一样的。
我认为该效果是由于 OpenCL 对象仍在范围内,因此没有在 for 循环之前被删除。由于需要考虑,它们可能会影响其他计算。例如,运行 您给出的示例在我的系统上产生了以下时间(g++ 4.2.1,O2 在 Mac OSX 上):
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: %d\n", 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 read\n", b);
}
printf("Sum: %d\n", sum);
}
我知道时间:
CL: 0.012s
Loop: 14.635s
Both: 14.648s
这似乎是线性相加。与对系统的其他影响相比,该影响非常小,例如从其他进程加载 CPU,但在添加匿名范围时它似乎消失了。如果它产生任何有趣的东西,我会做一些分析并将其添加为编辑。
这很有趣。这是因为 getc 在队列被实例化时成为线程安全版本,所以时间增加是锁的抓取释放周期 - 我不确定 why/how 这是否会发生,但这是决定性的一点带有英特尔 CPU 的 AMD OpenCL SDK。我很惊讶我和 OP 的时间基本相同。
https://software.intel.com/en-us/forums/topic/337984
您可以尝试通过将 getc 更改为 getc_unlocked 来解决此特定问题。
对我来说,它又回到了 930 毫秒 - 超过 750 毫秒的时间增加主要用于平台和上下文创建行。
我正在使用 OpenCL 开发一个 C++ 项目。我将 CPU 用作带有 intel OpenCL runtime
的 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: %d\n", 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 read\n", b);
}
printf("Sum: %d\n", sum);
}
这是一个简单的循环,一个字符一个字符地读取文件并将它们相加,这样编译器就不会尝试优化它。
我的系统是 Core i7-4770K,2TB HDD 16GB DDR3 运行ning Ubuntu 14.10。上面的程序以一个 100MB 的文件作为输入,大约需要 770 毫秒。这与我的硬盘速度一致。到目前为止一切顺利。
如果您现在反转注释并且 运行 只有 OpenCL 调用区域,则大约需要 200 毫秒。再一次,到目前为止,一切都很好。
Buf如果全部取消注释,程序耗时超过2000ms。我期望 770ms + 200ms,但它是 2000ms。您甚至可以注意到 for 循环中输出消息之间的延迟增加。这两个区域(OpenCL 调用和读取字符)应该是独立的。
我不明白为什么使用 OpenCL 会干扰简单的 C++ for 循环性能。这不是简单的 OpenCL 初始化延迟。
我正在编译这个例子:
g++ weird.cpp -O2 -lOpenCL -o weird
我也尝试过使用 Clang++,但结果是一样的。
我认为该效果是由于 OpenCL 对象仍在范围内,因此没有在 for 循环之前被删除。由于需要考虑,它们可能会影响其他计算。例如,运行 您给出的示例在我的系统上产生了以下时间(g++ 4.2.1,O2 在 Mac OSX 上):
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: %d\n", 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 read\n", b);
}
printf("Sum: %d\n", sum);
}
我知道时间:
CL: 0.012s
Loop: 14.635s
Both: 14.648s
这似乎是线性相加。与对系统的其他影响相比,该影响非常小,例如从其他进程加载 CPU,但在添加匿名范围时它似乎消失了。如果它产生任何有趣的东西,我会做一些分析并将其添加为编辑。
这很有趣。这是因为 getc 在队列被实例化时成为线程安全版本,所以时间增加是锁的抓取释放周期 - 我不确定 why/how 这是否会发生,但这是决定性的一点带有英特尔 CPU 的 AMD OpenCL SDK。我很惊讶我和 OP 的时间基本相同。
https://software.intel.com/en-us/forums/topic/337984
您可以尝试通过将 getc 更改为 getc_unlocked 来解决此特定问题。
对我来说,它又回到了 930 毫秒 - 超过 750 毫秒的时间增加主要用于平台和上下文创建行。