2018-06-05【没有对应头文件的.so文件怎么处理】

2018-06-05  本文已影响0人  巴普洛夫学习

想用SVML

在正常情况下,.so文件作为Runtime隐藏起来,程序员只要面向头文件编程即可。例如多线程库pthread在使用时只需要包含头文件pthread.h,在链接时加上-lpthread选项就可以。

但是,在某些情况下,能够获得的只有.so文件,而又没有类似的替代物,那也只能硬着头皮上了。具体到笔者的例子,是intel的svml库。

svml库是intel的向量指令库。它与一堆的SSE、AVX、MMX同样,属于SIMD指令的一部分。比较特别的是,它包含了三角函数、指数函数、双曲三角函数等指令。相比其他SIMD系列,它的intrinsic不对外授权,因此gcc自带了SSE、AVX等等,却不自带svml。

想用svml,那就下intel C++ compiler去吧。学生邮箱倒是可以免费,但是国内的学校好像不认;也可以作为开源开发者申请,但是要走审批流程,2个工作日起。

经过一番搜索,发现对外开放的intel OpenCL Runtime中包含了svml的二进制代码。在Ubuntu 18.04上下载并安装opencl_runtime_16.1.2_x64)rh_6.4.0.37.tgz中的内容后,在/opt/intel/opencl/lib64下将存在__ocl_svml_e9.so__ocl_svml_h8.so__ocl_svml_l9.so三个.so文件。我们想用的函数就在其中实现,只不过是机器码。

确定.so文件中的内容

使用nmobjdumpreadelf都可以查看.so文件中的内容。例如

值得一提的是,使用objdump可以反编译这个.so文件。执行$ objdump -D __ocl_svml_h8.so > output.asm可以看到反编译的结果。

asm.png

我想用的函数就是图中的__ocl_svml_h8_sinf8,也就是使用256bit的大宽度SIMD寄存器一次计算8个32位浮点数的sin值。

通过反汇编,可以发现其底层实现并不是一条指令,而是一堆指令的集合。换句话说,其实是官方写了一个库。由于有h8,e9,l9三个文件,因此把它们的反汇编都看了一遍,发现实现都非常长,因此基本确定sinf8是软件实现。

真是失望啊,回头还是用查表法解决问题吧。不过事情还得继续,.so还是得用起来。

怎么使用.so文件

思路很简单。造一个头文件。svml的用法在intel intrinsics guide中有提到。


sinf8

很直接的思路是直接在头文件中写入一行:__m256 _mm256_sin_ps(__256 a);。但是显然不行。.so文件中的函数是通过符号名来检索的,我们需要在头文件中生成一个符号名与.so文件相同的函数定义。

C的name mangling

name mangling是指C/C++在编译过程中,将函数名转为符号名的一套规则。不同的编译器通常不同。而在我们的.so文件中出现的符号名明显和C不是一个画风。

因此,通过符号名反推函数名是不行的。我们需要的是钦定任意一个函数的符号名的办法。而gcc提供有这样的功能。下面这行代码就声明了一个符号名为“functiona”,函数名为func的函数:

void func() __asm__("functiona");

最后解决

于是,操作就是很简单了。
我们写了个测试文件:

#include <immintrin.h>
#include <cstdio>

__m256 __ocl_svml_e9_sinf8(__m256 ) __asm__("__ocl_svml_e9_sinf8");

int main(){
  __m256 a, b;
  a = _mm256_set1_ps(10.0);
  b = __ocl_svml_e9_sinf8(a);
  float* data = (float*) aligned_alloc(64, 256/8);

  _mm256_store_ps(data, b);

  printf("%f\n", data[0]);
}

编译使用的命令是:

$ gcc -mavx test.cpp __ocl_svml_e9.so

编译生成了a.out文件,运行后报错,显示找不到.so文件。只要将报错信息显示的so文件拷贝成需要的名字,放到/usr/lib或者其他合适的位置即可。

上一篇下一篇

猜你喜欢

热点阅读