Linux C++程序使用Intel OpenCL的问题

Intel OpenCL 问题

最近想在 Linux 环境下使用 Intel 核显测试我们的某个程序,该程序是用 C++ 写的,主要调用 OpenCL 库使用 GPU 进行运算。之前我用 AMD 和 NVIDIA 的显卡都测试过没有问题,还以为会比较顺利,结果在程序刚开始调用 clGetPlatformIDs 检测 OpenCL platform 的时候就直接报错退出了。

我的测试环境使用的是 Intel i3-6100 CPU,该处理器自带 Intel HD Graphics 530 核显,操作系统则是 CentOS 6.9 64 位(比较老,为了和使用环境一致),安装的也是 Intel 官方网站上提供的 Intel OpenCL 2.0 Driver(SRB 5.0 版本),具体可以参考 Intel 官网的 OpenCL™ Drivers and Runtimes for Intel® Architecture 介绍。

值得一提的是 Intel OpenCL Driver 安装完成之后,运行 clinfo 命令是可以正常识别到核显的,而且我使用另外一个通过 OpenCL 进行计算的 C 程序来测试又是完全没问题的。

调试分析

初步考虑可能 Intel OpenCL 在此系统和 C++ 程序存在兼容性问题,为此我先写了一个最简单的 C++ 程序来检测 OpenCL platform:

#include <iostream>
#include <string>
#include <CL/cl.h>

void init_cl()
{
	cl_uint plats = 0;
	std::cout << "Begin get OpenCL platform." << std::endl;
	clGetPlatformIDs(0, NULL, &plats);
	std::cout << "OpenCL platforms: " << plats << std::endl;
}

int main(int argc, char **argv)
{
	std::string str = "cl";
	init_cl();
	return 0;
}

使用 g++ 编译该测试程序,编译时启用优化:

[root@localhost ~]# g++ -O2 -o cl cl.cpp -lOpenCL -lstdc++

测试程序使用 AMD 和 NVIDIA 显卡的 OpenCL 库都可以正常检测到 platform,使用 Intel 核显则会遇到下面的错误:

[root@localhost tmp]# ./cl
Begin get OpenCL platform.
*** glibc detected *** ./cl: free(): invalid pointer: 0x0000003de60f32c0 ***
======= Backtrace: =========
/lib64/libc.so.6[0x328cc75716]
/usr/lib64/libstdc++.so.6(_ZNSs6assignERKSs+0x85)[0x3de5e9d565]
/opt/intel/opencl/libigdmcl.so(+0x2040ba)[0x7f75360a00ba]
/opt/intel/opencl/libigdmcl.so(+0x1e6cc2)[0x7f7536082cc2]
/lib64/ld-linux-x86-64.so.2[0x328c40e4ef]
/lib64/ld-linux-x86-64.so.2[0x328c412bf2]
/lib64/ld-linux-x86-64.so.2[0x328c40e106]
/lib64/ld-linux-x86-64.so.2[0x328c41245a]
/lib64/libdl.so.2[0x328c800f66]
/lib64/ld-linux-x86-64.so.2[0x328c40e106]
/lib64/libdl.so.2[0x328c80129c]
/lib64/libdl.so.2(dlopen+0x31)[0x328c800ee1]
/opt/intel/opencl/libigdrcl.so(+0x124bd3)[0x7f7538082bd3]
/opt/intel/opencl/libigdrcl.so(+0xf4890)[0x7f7538052890]
/opt/intel/opencl/libigdrcl.so(+0xf4e26)[0x7f7538052e26]
/opt/intel/opencl/libigdrcl.so(+0xe8982)[0x7f7538046982]
/opt/intel/opencl/libigdrcl.so(+0xd0f3c)[0x7f753802ef3c]
/opt/intel/opencl/libigdrcl.so(clIcdGetPlatformIDsKHR+0x2b)[0x7f753801a0ab]
/opt/intel/opencl/libIntelOpenCL.so(+0x234e4)[0x7f75388c34e4]
/opt/intel/opencl/libIntelOpenCL.so(+0x3f20)[0x7f75388a3f20]
/usr/lib64/libOpenCL.so.1(+0x298b)[0x7f7538f0b98b]
/usr/lib64/libOpenCL.so.1(+0x4907)[0x7f7538f0d907]
/lib64/libpthread.so.0(pthread_once+0x53)[0x7f7538adfe03]
/usr/lib64/libOpenCL.so.1(clGetPlatformIDs+0x11)[0x7f7538f0bf21]
./cl[0x400c6e]
./cl[0x400d31]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x328cc1ec9d]
./cl[0x400b09]
======= Memory map: ========
00400000-00402000 r-xp 00000000 00:11 10291                              /tmp/cl
00601000-00602000 rw-p 00001000 00:11 10291                              /tmp/cl
0179d000-017be000 rw-p 00000000 00:00 0                                  [heap]
328c400000-328c420000 r-xp 00000000 fd:00 7124                           /lib64/ld-2.12.so
328c61f000-328c620000 r--p 0001f000 fd:00 7124                           /lib64/ld-2.12.so
328c620000-328c621000 rw-p 00020000 fd:00 7124                           /lib64/ld-2.12.so
328c621000-328c622000 rw-p 00000000 00:00 0
328c800000-328c802000 r-xp 00000000 fd:00 7116                           /lib64/libdl-2.12.so
328c802000-328ca02000 ---p 00002000 fd:00 7116                           /lib64/libdl-2.12.so
328ca02000-328ca03000 r--p 00002000 fd:00 7116                           /lib64/libdl-2.12.so
328ca03000-328ca04000 rw-p 00003000 fd:00 7116                           /lib64/libdl-2.12.so
328cc00000-328cd87000 r-xp 00000000 fd:00 6933                           /lib64/libc-2.12.so
328cd87000-328cf87000 ---p 00187000 fd:00 6933                           /lib64/libc-2.12.so
328cf87000-328cf8b000 r--p 00187000 fd:00 6933                           /lib64/libc-2.12.so
328cf8b000-328cf8c000 rw-p 0018b000 fd:00 6933                           /lib64/libc-2.12.so
328cf8c000-328cf91000 rw-p 00000000 00:00 0
328d400000-328d483000 r-xp 00000000 fd:00 6818                           /lib64/libm-2.12.so
328d483000-328d682000 ---p 00083000 fd:00 6818                           /lib64/libm-2.12.so
328d682000-328d683000 r--p 00082000 fd:00 6818                           /lib64/libm-2.12.so
328d683000-328d684000 rw-p 00083000 fd:00 6818                           /lib64/libm-2.12.so
328d800000-328d807000 r-xp 00000000 fd:00 7077                           /lib64/librt-2.12.so
328d807000-328da06000 ---p 00007000 fd:00 7077                           /lib64/librt-2.12.so
328da06000-328da07000 r--p 00006000 fd:00 7077                           /lib64/librt-2.12.so
328da07000-328da08000 rw-p 00007000 fd:00 7077                           /lib64/librt-2.12.so
3de5e00000-3de5ee8000 r-xp 00000000 00:11 9623                           /usr/lib64/libstdc++.so.6.0.13
3de5ee8000-3de60e8000 ---p 000e8000 00:11 9623                           /usr/lib64/libstdc++.so.6.0.13
3de60e8000-3de60ef000 r--p 000e8000 00:11 9623                           /usr/lib64/libstdc++.so.6.0.13
3de60ef000-3de60f1000 rw-p 000ef000 00:11 9623                           /usr/lib64/libstdc++.so.6.0.13
3de60f1000-3de6106000 rw-p 00000000 00:00 0
7f7535c86000-7f7535c9b000 r-xp 00000000 fd:00 7143                       /lib64/libz.so.1.2.3
7f7535c9b000-7f7535e9a000 ---p 00015000 fd:00 7143                       /lib64/libz.so.1.2.3
7f7535e9a000-7f7535e9b000 r--p 00014000 fd:00 7143                       /lib64/libz.so.1.2.3
7f7535e9b000-7f7535e9c000 rw-p 00015000 fd:00 7143                       /lib64/libz.so.1.2.3
7f7535e9c000-7f7536b3a000 r-xp 00000000 07:05 10                         /opt/intel/opencl/libigdmcl.so
7f7536b3a000-7f7536d3a000 ---p 00c9e000 07:05 10                         /opt/intel/opencl/libigdmcl.so
7f7536d3a000-7f7536d91000 r--p 00c9e000 07:05 10                         /opt/intel/opencl/libigdmcl.so
7f7536d91000-7f7536d93000 rw-p 00cf5000 07:05 10                         /opt/intel/opencl/libigdmcl.so
7f7536d93000-7f7536d9f000 rw-p 00000000 00:00 0
7f7536d9f000-7f7536da7000 r-xp 00000000 fd:00 13144                      /usr/lib64/libpciaccess.so.0.11.1
7f7536da7000-7f7536fa7000 ---p 00008000 fd:00 13144                      /usr/lib64/libpciaccess.so.0.11.1
7f7536fa7000-7f7536fa8000 rw-p 00008000 fd:00 13144                      /usr/lib64/libpciaccess.so.0.11.1
7f7536fa8000-7f7537351000 r-xp 00000000 07:05 12                         /opt/intel/opencl/libmd.so
7f7537351000-7f7537550000 ---p 003a9000 07:05 12                         /opt/intel/opencl/libmd.so
7f7537550000-7f7537554000 r--p 003a8000 07:05 12                         /opt/intel/opencl/libmd.so
7f7537554000-7f7537556000 rw-p 003ac000 07:05 12                         /opt/intel/opencl/libmd.so
7f7537556000-7f753755d000 rw-p 00000000 00:00 0
7f753755d000-7f753755e000 ---p 00000000 00:00 0
7f753755e000-7f7537f5e000 rw-p 00000000 00:00 0
7f7537f5e000-7f7538152000 r-xp 00000000 07:05 11                         /opt/intel/opencl/libigdrcl.so
7f7538152000-7f7538351000 ---p 001f4000 07:05 11                         /opt/intel/opencl/libigdrcl.so
7f7538351000-7f7538357000 r--p 001f3000 07:05 11                         /opt/intel/opencl/libigdrcl.so
7f7538357000-7f7538894000 rw-p 001f9000 07:05 11                         /opt/intel/opencl/libigdrcl.so
7f7538894000-7f75388a0000 rw-p 00000000 00:00 0
7f75388a0000-7f75388cb000 r-xp 00000000 07:05 2                          /opt/intel/opencl/libIntelOpenCL.so
7f75388cb000-7f7538acb000 ---p 0002b000 07:05 2                          /opt/intel/opencl/libIntelOpenCL.so
7f7538acb000-7f7538acc000 r--p 0002b000 07:05 2                          /opt/intel/opencl/libIntelOpenCL.so
7f7538acc000-7f7538acd000 rw-p 0002c000 07:05 2                          /opt/intel/opencl/libIntelOpenCL.so
7f7538acd000-7f7538ad3000 rw-p 00000000 00:00 0
7f7538ad3000-7f7538aea000 r-xp 00000000 fd:00 7139                       /lib64/libpthread-2.12.so
7f7538aea000-7f7538cea000 ---p 00017000 fd:00 7139                       /lib64/libpthread-2.12.so
7f7538cea000-7f7538ceb000 r--p 00017000 fd:00 7139                       /lib64/libpthread-2.12.so
7f7538ceb000-7f7538cec000 rw-p 00018000 fd:00 7139                       /lib64/libpthread-2.12.so
7f7538cec000-7f7538cf2000 rw-p 00000000 00:00 0
7f7538cf2000-7f7538d08000 r-xp 00000000 fd:00 7140                       /lib64/libgcc_s-4.4.7-20120601.so.1
7f7538d08000-7f7538f07000 ---p 00016000 fd:00 7140                       /lib64/libgcc_s-4.4.7-20120601.so.1
7f7538f07000-7f7538f08000 rw-p 00015000 fd:00 7140                       /lib64/libgcc_s-4.4.7-20120601.so.1
7f7538f08000-7f7538f09000 rw-p 00000000 00:00 0
7f7538f09000-7f7538f10000 r-xp 00000000 fd:00 12875                      /usr/lib64/libOpenCL.so.1
7f7538f10000-7f753910f000 ---p 00007000 fd:00 12875                      /usr/lib64/libOpenCL.so.1
7f753910f000-7f7539110000 rw-p 00006000 fd:00 12875                      /usr/lib64/libOpenCL.so.1
7f7539110000-7f7539111000 rw-p 00000000 00:00 0
7ffe17048000-7ffe17069000 rw-p 00000000 00:00 0                          [stack]
7ffe170de000-7ffe170e1000 r--p 00000000 00:00 0                          [vvar]
7ffe170e1000-7ffe170e3000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted

从错误输出来看是 clGetPlatformIDs 调用过程中 Intel OpenCL Driver 内部就出现问题了,释放了无效的指针导致程序异常退出。

几番修改测试之后我发现两个现象:

  1. 如果 g++ 编译时不使用优化选项(-O2 参数去掉或者改为 -O0),则程序可以正常运行;
  2. 如果去掉第 15 行的 std::string 变量定义代码,即使保持优化选项开启并链接 libstdc++ 库,程序也可以正常运行。

看起来只要 libstdc++ 库确实被使用了,优化过的 C++ 程序调用 Intel OpenCL 就会出现异常,而由于我们的程序需要开启编译器优化,因此要找到其它解决办法。

解决方法

局部关闭编译器优化

我们可以在整个程序开启编译器优化的情况下,只关闭对调用 OpenCL 库那部分代码的优化。下面是两种局部关闭编译器优化的方法,通过这两种方法修改的测试程序编译之后都可以正常运行。

pragma 指令

把测试程序的代码改成这样:

#pragma GCC push_options
#pragma GCC optimize ("O0")
void init_cl()
{
	...
}
#pragma GCC pop_options

使用 pragma 指令指定 #pragma GCC push_options#pragma GCC pop_options 包起来的这部分代码的编译选项,好处是可以包含多个函数。

__attribute__ 属性

我们也可以直接通过 GNU 编译器的 __attribute__ 编译器属性指定某个函数的优化选项:

void __attribute__((optimize("O0"))) init_cl()

升级 libstdc++ 库

不过有点悲剧的是我们的 C++ 程序通过上面两种方法关闭 OpenCL 调用部分代码的编译器优化之后,运行之后仍然异常退出。另外我把程序挪到 CentOS 7.1 64 位系统上运行则没有任何问题,最终考虑要通过升级 libstdc++ 库(CentOS 6.9 上的是 4.4.7-18 版本)来解决此问题。

本来打算通过手工编译新版本 GCC 来生成新版本的 libstdc++ 库,不过还好搜索之后发现网上已经有现成的 CentOS 6 上的新版本 libstdc++ 了,具体可以参考 SAP 公司提供的 compat-sap-c++ for RedHat 6.7+

按照 SAP 网站上的说明安装 compat-sap-c++ 包,我们也可以直接替换系统链接:

[root@localhost ~]# curl -O ftp://ftp.pbone.net/mirror/ftp.scientificlinux.org/linux/scientific/6.7/x86_64/updates/fastbugs/compat-sap-c++-4.8.2-16.el6.x86_64.rpm
[root@localhost ~]# rpm -ivh compat-sap-c++-4.8.2-16.el6.x86_64.rpm
[root@localhost ~]# ln -sfn /opt/rh/SAP/lib64/compat-sap-c++.so /usr/lib64/libstdc++.so.6

替换 libstdc++ 为 4.8.2-16 版本之后再运行 OpenCL 测试程序就会发现即使开启编译器优化也没有问题了:

[root@localhost tmp]# ./cl
Begin get OpenCL platform.
OpenCL platforms: 1

看来 Intel 官网上介绍的 Intel OpenCL Driver 只验证了 CentOS 7.2、7.3 及 Ubuntu 16.04 系统下的兼容性还算靠谱的,如果要在老一点的 Linux 系统上使用 Intel OpenCL 还是需要尽量避免碰坑,最后祝大家玩的开心。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*