您尚未登录。

#1 2016-06-19 12:27:39

zsrkmyn
lazy...
注册时间: 2013-05-05
帖子: 331

给 octave 添上 Intel® mkl

啊因为博客关了好久了,昨天折腾了一下 Intel® mkl,感觉过程还是蛮有意思的,想记录下来又没地方写,想想不如写到社区,还可以和大家分享一下。

0. 前言
由于 octave 的运算速度实在是太慢了,和 matlab 相比相差甚远,所以想改进一下!正好差两天毕业,趁着还有 edu 邮箱,所以申请了个 parallel_studio_xe_2016 来体验一发,当然主要用到了里面的 mkl。本来还打算用 icc 来编译一个 octave 的,但是苦于连 ./configure 阶段都没法通过,只好作罢……

1. 简介
octave 依赖 lapack blas fftw 三个包进行线性代数运算和傅立叶变换。

lapack 和 blas 默认是单线程的,无法忍受啊!
archlinuxcn 源(以及 AUR)提供了 openblas,多线程,速度要比 lapack 和 blas 快很多了!
community 源提供了 atlas-lapack-base,这是一个适配大多数处理器的 atlas lapack,多线程,相比 openblas 性能差了一些。
由于 atlas-lapck-base 是适配于大多数处理器的包,所以并不能发挥出 atlas lapack 的最佳性能。所以 AUR 提供了 atlas-lapack 包,这个包需要在本地编译,编译过程中会指定处理器个数,编译好之后测试了一下,相比 atlas-lapack-base 性能提示还是蛮大的,比 openblas 稍好一点。

fftw 包默认是多线程,但是后期测试性能还是不如 Intel® mkl 啊。

2. 过程
首先是下载了最新的 parallel_studio_xe_2016, 这玩意 3.9GiB 真够大的!然后通过 edu 邮箱获取了试用序列号,申请了正版证书。 intel 还是蛮良心的!

本来想直接用 icc/ifort 编译 octave 然后直接链接 Intel® mkl 库的,但是这篇文章[1]的发表日期是 2011 年,已经过时了诶!无奈自己 hack octave 的 configure 文件半天,但是发现只要编译过程中加了 -lsvml (由 intel-compiler-base 提供该动态库)选项,无论 icc/gcc,程序就会吐核,而且吐核的地方居然是在 main 前,猜测大概在 load 库的时候就挂了,所以编译 octave 的 ./configure 过程中各种测试没法通过,Intel 的一堆库都是私有软件,完全不知道问题出在哪里,最后不得不放弃 :'(

后来想直接找 Intel® mkl 中的 lib*{blas,lapack}.so 来替换掉 /usr/lib 中的 lib{blas,lapack}.so 以达到利用 mkl 的效果,无奈只找到 libmkl_blas95_{i,}lp64.a libmkl_lapack95_{i,}lp64.a libmkl_core.a 几个看起来还有点像的的文件,然而这全都是静态库啊!

再次准备放弃的时候,无意间看到了这篇文章[2]!这是什么?*How to create a standalone MKL version of BLAS and LAPACK shared libraries?* 啊哈哈!不正是我要找的东西吗!

大概的原理是,由于 Intel® mkl 的几个核心库是 PIC 的,所以可以从中构建自己需要的 .so 动态库[3]!

所以呢,照着这篇文章的内容,很容易就搞到了需要的几个库!完全没有难度嘛!首先用之前下好的 parallel_studio_xe_2016 和 AUR 的 intel-parallel-studio-xe 打好包,安装其中的 intel-mkl 和 intel-compiler-base 两个包,其实不装后者似乎也没关系,后者提供了几个软链,方便后面的 cp 操作,仅此而已……

建立一个工作目录,叫 intel-mkl 好了!

mkdir intel-mkl && cd intel-mkl

然后把需要的东西拷贝过来,intel 比较良心,已经把 blas、cblas 和 lapack 的符号表列表写好了,所以直接 cp 过来用就好了!

cp /opt/intel/mkl/tools/builder/{makefile,blas_example_list,cblas_example_list,dft_example_list,lapack_example_list} .

之后 make 三次就可以得到需要的 .so 了!

make libintel64 export=blas_example_list interface=lp64  threading=parallel name=libblas_mkl
make libintel64 export=lapack_example_list interface=lp64  threading=parallel name=liblapack_mkl
make libintel64 export=cblas_example_list interface=lp64  threading=parallel name=libcblas_mkl

其实 octave 不依赖 cblas,不过既然有,顺便提取一下好了!关于上面的参数,这里[5]和 make help 都有详细的解释。关于 lp64 和 ilp64 的区别,这里[6]有讲,大概就是 lp64 的 int 类型是 32 位的,ilp64 的 int 是 64 位的。

接下来就要提取 fftw3 了,以下提取方法实在很正确,所以如果各位有什么更好的方法欢迎告诉我呀!fftw 一共提供了 9 个 .so 文件,如下:

$ ll /usr/lib/libfftw3{,l,f}{,_omp,_threads}.so |awk '{print $9$10$11}'
/usr/lib/libfftw3f_omp.so->libfftw3f_omp.so.3.4.4*
/usr/lib/libfftw3f.so->libfftw3f.so.3.4.4*
/usr/lib/libfftw3f_threads.so->libfftw3f_threads.so.3.4.4*
/usr/lib/libfftw3l_omp.so->libfftw3l_omp.so.3.4.4*
/usr/lib/libfftw3l.so->libfftw3l.so.3.4.4*
/usr/lib/libfftw3l_threads.so->libfftw3l_threads.so.3.4.4*
/usr/lib/libfftw3_omp.so->libfftw3_omp.so.3.4.4*
/usr/lib/libfftw3.so->libfftw3.so.3.4.4*
/usr/lib/libfftw3_threads.so->libfftw3_threads.so.3.4.4*

由于我比较懒,又分不清这些 l 啦 f 啦 threads 啦 omp 啦有什么区别,所以索性全部提取到一个 .so 里好了(雾),如果这个方法不对求打脸 QuQ。

首先从这堆 .so 里提取符号表,生成 fftw_list 文件:

nm -D /usr/lib/libfftw3{,l,f}{,_omp,_threads}.so |grep -v ' w ' | grep -v ' U '| sed -n 's#.* \([[:alnum:]_]\+$\)#\1#p' |sort |uniq > fftw_list

然后像之前一样 make 就好啦!其中两个 grep 分别是去除弱符号和未定义符号,否则会出现 undefined symbol: _Jv_RegisterClasses 之类的错误……

make libintel64 export=fftw_list interface=lp64 threading=parallel name=libfftw3_mkl

得到 libfftw3_mkl.so 之后用 nm -D 查看了一下符号表,发现似乎比之前生成的 fftw_list 里面少了一些东西,现在还不知道有没有影响……反正 octave 的 fft/fft2/ifft/ifft2 都是能工作的 ~\(≧▽≦)/~

此时我们就得到了 4 个需要的 .so,分别是 libblas_mkl.so libcblas_mkl.so libfftw3_mkl.so liblapack_mkl.so,然后呢? strip 这 4 个文件,扔到 /usr/lib 里,然后再做几个软链接就完成啦!

3. Tips & Tricks
3.1 OpenMP 依赖
其实生成的这几个文件,可能会依赖 Intel 的 OpenMP 库:

$ ldd libblas_mkl.so
	...
	libiomp5.so => /opt/intel/lib/libiomp5.so (0x00007f79b66d5000)
	...

我可不想用这几个库的时候还要安装 intel-compiler-base 这一大坨依赖!这是由于在 make 的时候没有安装 OpenMP 造成的,所以只需要安装 openmp 就好啦~

$ sudo pacman -S openmp --asdeps
  ...
$ make ...
$ ldd libblas_mkl.so
	...
	libiomp5.so => /usr/lib/libiomp5.so (0x00007f560b86b000)
	...

3.2 写一个 PKGBUILD
由于用 pacman 安装软件的时候会有依赖要求,所以写一个 PKGBUILD 来提供 lapack cblas blas 和 fftw 的依赖啦,还可以统一管理文件!

pkgname=libmkl
pkgver=11.3.3
pkgrel=1
pkgdesc="dynamic libraries extracted from intel mkl library, privides fftw, blas, cblas and lapack"
arch=('x86_64')
depends=('openmp')
url=""
license=('custom')
provides=('lapack' 'cblas' 'blas' 'fftw')
conflicts=('lapack' 'cblas' 'blas' 'fftw')
source=("libmkl.tar")
md5sums=('460f3cfbf0af09628a17e1ddde78631f')

PKGEXT=.pkg.tar

package() {
	cd "$pkgname"
	mkdir -p "$pkgdir"/usr/lib
	cp -P * "$pkgdir"/usr/lib
}

其中 source 里的 libmkl.tar 当然就是包含四个 .so 和一堆符号链接的 tarball 啦。大概长这个样子,里面除了 *_mkl.so 外都是符号链接。

$ tar -tf libmkl.tar
libmkl/
libmkl/liblapack_mkl.so
libmkl/libfftw3_mkl.so
libmkl/libcblas_mkl.so
libmkl/libblas_mkl.so
libmkl/libfftw3.so.3
libmkl/libfftw3_omp.so.3
libmkl/libfftw3_threads.so.3
libmkl/libfftw3f.so.3
libmkl/libfftw3f_omp.so.3
libmkl/libfftw3f_threads.so.3
libmkl/libfftw3l.so.3
libmkl/libfftw3l_omp.so.3
libmkl/libfftw3l_threads.so.3
libmkl/libfftw3f_omp.so
libmkl/libfftw3f.so
libmkl/libfftw3f_threads.so
libmkl/libfftw3l_omp.so
libmkl/libfftw3l.so
libmkl/libfftw3l_threads.so
libmkl/libfftw3_omp.so
libmkl/libfftw3.so
libmkl/libfftw3_threads.so
libmkl/libblas.so
libmkl/libcblas.so
libmkl/liblapack.so
libmkl/libf77blas.so
libmkl/liblapack.so.3
libmkl/libblas.so.3

3.3 使用 OpenMP 的多线程
参考这里[7]啦。其中比较主要的几个环境变量是:

OMP_NUM_THREADS: 设置 OMP 的线程数量
MKL_NUM_THREADS: 设置 MKL 的线程数量
MKL_DYNAMIC: 设置为 true 会 reduce possible oversubscription from MKL threading[7]。

注意缺省状态或设置 MKL_DYNAMIC 为 true 会导致线程数量不是最大。之前我设置为 true 之后 octave 进行远算时始终只有两个线程,设置为 false 之后跑满了 4 个线程。然而!!!我在跑满 4 个线程和跑 2 个线程运行矩阵乘法远算,速度居然是一样的!所以感觉设置为 true 来 reduce oversubscription 应该还是有用的。

关于 OMP_NUM_THREADS 和 MKL_NUM_THREADS 两者效果上的区别我也不是太懂,有人说设置后者会屏蔽前者。但是[7]包含如下一段话:

Example:
Calls MKL routines dsyev from one own OpenMP* parallel region with 2 OpenMP threads and 8 MKL threads.
MKL_DYNAMIC=false
OMP_NESTED=true
OMP_NUM_THREADS=2 MKL_NUM_THREADS=8

所以我也不太清楚啦~反正我二者设置的都是 4。

4. 性能对比
有空再补上吧,写不动了……

5. 分享制作好的 .so
等我先看看 EULA 看能不能分发再说……

6. 可能的 bug
6.1 当然是打好的 PKGBUILD 没有各种 .h 啦!所以虽然写了 provides,但是如果本地要编译某些依赖 blas cblas lapack fftw 的程序的时候肯定是找不到头文件的啦!

6.2 就是制作 libfftw3*.so 的手法太脏了……求修改意见……

好了就这样吧,先写到这里吧。

References:
[1] https://software.intel.com/en-us/articl … gnu-octave
[2] http://wiki.science.ru.nl/cncz/Intel_co … raries_.3F
[3] http://www.theochem.ru.nl/composerxe/Do … /index.htm
[4] https://aur.archlinux.org/pkgbase/intel … studio-xe/
[5] http://www.theochem.ru.nl/composerxe/Do … meters.htm
[6] https://software.intel.com/en-us/node/528682
[7] https://software.intel.com/en-us/articl … plications

2016-11-02 Update:
今天在准备打算学习 numpy 的时候出现了问题!Intel 给的几个 *_example_list 中定义的符号表不全,导致在 import numpy 的时候会提示 undefined symbol,所以又稍微折腾了一下。缺少的几个 symbol 如下:

cblas_zdotc_sub
cblas_zdotu_sub
cblas_cdotc_sub
cblas_cdotu_sub
dcopy_

前四个在 cblas 库中,最后一个在 blas 库中。所以只需要在 make 之前,分别在 cblas_example_list 和 blas_example_list 文件中添加前四个和最后一个符号,再 make 就好了。

另外,numpy 只会加载 liblapack.so 库,而我们生成的 liblapack.so 并没有动态链接到 libblas.so, 所以完成上述步骤后 numpy 依然不能找到 dcopy_ 符号,因此还需要重新提取 liblpack.so 使其动态链接到 libblas.so。所以在生成 liblpack.so 这步需要修改 makefile,在 makefile 中的 gcc 编译选项的 -L ... $(LIBM) 选项后添加 -lblas。

2016-11-03 Update:
今天发现 intel 给的 example_list 少了好多好多符号,建议还是按照生成 fftw_list 的方法重新生成 list。

最近编辑记录 zsrkmyn (2018-05-02 12:50:24)

离线

#2 2016-06-19 17:16:08

zsrkmyn
lazy...
注册时间: 2013-05-05
帖子: 331

Re: 给 octave 添上 Intel® mkl

话说 BBCode 的 code 标签代码高亮可以调么 =.=

离线

#3 2016-06-19 19:27:41

SilverRainZ
成为非人类
所在地: Arch Linux CN Community
注册时间: 2015-05-01
帖子: 110
个人网站

Re: 给 octave 添上 Intel® mkl

阿森汪好棒!
然而看不懂。

离线

#4 2016-06-19 19:55:44

依云
会员
所在地: a.k.a. 百合仙子
注册时间: 2011-08-21
帖子: 8,432
个人网站

Re: 给 octave 添上 Intel® mkl

zsrkmyn 说:

话说 BBCode 的 code 标签代码高亮可以调么 =.=

不能……欢迎 pr~

离线

页脚