问题描述
我正在使用英特尔数学内核库 (MKL) 用 C 语言编写物理模拟,并希望使用 cython 从 python 代码中直接调用它。 cython 编译本身可以工作(如果示例中不包含 MKL,则程序运行不会出错)并且如果我直接在 gcc 中使用
编译我的 C 代码gcc -O3 -Wall -m64 -I"${MKLROOT}/include" bar.c -L${MKLROOT}/lib/intel64 -Wl,--no-as-needed -lmkl_intel_lp64 -lmkl_sequential -lmkl_core -lpthread -lm -ldl
效果很好。编译器标志由 MKL Link line Advisor 生成。 但是,如果我现在尝试使用 cython 编译相同的代码,则会收到错误消息
INTEL MKL ERROR: /opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_avx2.so.1: undefined symbol: mkl_sparse_optimize_bsr_trsm_i8. Intel MKL Fatal error: Cannot load libmkl_avx2.so.1 or libmkl_def.so.1.
我还尝试将我的程序(没有主程序)编译到共享库(.so
)中,以便在 cython 之外完成 MKL 链接,然后直接链接到它,但是在将该库摆弄成LD_LIBRARY_PATH
我刚刚又出现了同样的行为。
知道如何使链接正确吗?
我可以将使用过的 cblas_
函数替换为不同的函数(例如 cblas_drot
和更多尝试过的函数)并得到相同的错误。
我已经阅读了很多其他问题(很多关于 anaconda 中的 MKL,我的 MKL 是手动安装在 /opt
中的,如上面的路径所示),包括 this,它试图在 Java 中使用 MKL 和得到了同样的错误。
我可以重现关于 nm
语句(在 libmkl_avx2.so.1
中未定义,但在 libmkl_gnu_thread.so
中定义)的相同结果,但我未能将那个问题的答案应用于我的问题。如果我尝试在下面显示的 -lmkl_gnu_thread
脚本中添加 setup.py
,我会得到不同的未实现的依赖项,通过还包括 -fopenmp
来修复旧错误...
更多信息和使用过的文件
LD_LIBRARY_PATH=/opt/intel/oneapi/mkl/latest/lib/intel64:/opt/intel/oneapi/compiler/2021.1.1/linux/lib:/opt/intel/oneapi/compiler/2021.1.1/linux/lib/x64:/opt/intel/oneapi/compiler/2021.1.1/linux/lib/emu:/opt/intel/oneapi/compiler/2021.1.1/linux/compiler/lib/intel64_lin:/opt/intel/oneapi/compiler/2021.1.1/linux/compiler/lib:/opt/intel/oneapi/tbb/2021.1.1/env/../lib/intel64/gcc4.8
(由 mkl setvars 脚本设置,由 echo $LD_LIBRARY_PATH
获得)
python 可能会对这个变量做些什么奇怪的事情?
readelf -d $MKLROOT/lib/intel64/libmkl_avx2.so.1 | grep NEEDED 0x0000000000000001 (NEEDED)
返回 Shared library: [libdl.so.2]
,表明它声称只依赖于 libdl
(我没有找到我从哪里得到那个命令的问题,但它也是针对这个主题的,只是文件名中缺少 .1)
使用过的文件
setup.py
>(包含第二个版本作为 comamnd,可以这样运行)
from Cython.distutils import build_ext
#from Cython.Build import cythonize
from distutils.extension import Extension
from distutils.core import setup
import numpy
extensions = [
Extension("foo",["foo.pyx"],include_dirs=[numpy.get_include()],extra_compile_args=["-Wall","-m64","-I\"${MKLROOT}/include\""],extra_link_args=["-fopenmp","-L${MKLROOT}/lib/intel64","-Wl,--no-as-needed","-lmkl_gnu_thread","-lmkl_intel_lp64","-lmkl_sequential","-lmkl_core","-lpthread","-lm","-ldl"])
]
for e in extensions:
e.cython_directives = {'language_level': "3"} #all are Python-3
# produce the same behavIoUr,first:
setup(ext_modules=cythonize(extensions))
#second
#setup(ext_modules=cythonize(extensions),# cmdclass = {'build_ext':build_ext})
用于
python setup.py build_ext --inplace
from Cython.Build import cythonize
from distutils.extension import Extension
from distutils.core import setup
import numpy
extensions = [
Extension("foo",include_dirs=[numpy.get_include(),"\"${MKLROOT}/include\""],libraries=["mkl_intel_lp64","mkl_sequential","mkl_core","pthread","m","dl"],library_dirs=["${MKLROOT}/lib/intel64"],"-m64"],extra_link_args=["-Wl,])
]
for e in extensions:
e.cython_directives = {'language_level': "3"} #all are Python-3
setup(ext_modules=cythonize(extensions))
foo.pyx
cimport numpy as np
import numpy as np
import ctypes
cdef extern from "bar.c":
void double_elements(int n,double* vec_y)
def func(np.ndarray[np.double_t,ndim=1] y not None):
double_elements(<int> y.size//2,<double*> <size_t> y.__array_interface__['data'][0])
return y
bar.c
#include <mkl.h>
#include <stdio.h>
void double_elements(int n,double* x) {
cblas_dscal(n,2.,x,1);
}
#ifndef PY_VERSION_HEX // compile the main only,if not using cython
int main() {
double x[2] = {1.,2.};
double_elements(2,x);
printf("%g %g\n",x[0],x[1]);
return 0;
}
#endif
运行.py
>import numpy as np
import foo
x = np.array([1.,2.])
y = foo.func(x)
print(x)
print(y)
解决方法
我已经通过使用 MKL 的静态链接解决了这个问题。因为我也没有在 setup.py 脚本中应用来自静态链接(它使用组)的所有命令,所以我已经切换到完全自己编译它
gcc -O3 -Wall -m64 -I"${MKLROOT}/include" -c bar.c -o build/bar.o
cythonize foo.pyx
# exchange /path/to/numpy/ by value given by numpy.get_include()
gcc -Wall -O2 -fstack-protector-strong -fwrapv -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIC -I/path/to/numpy/core/include -I/usr/include/python3.8 -c foo.c -o build/foo.o -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION
gcc -shared build/foo.o build/bar.o -o foo.cpython-38-x86_64-linux-gnu.so -Wl,--start-group ${MKLROOT}/lib/intel64/libmkl_intel_lp64.a ${MKLROOT}/lib/intel64/libmkl_sequential.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group -lpthread -lm -ldl
这对应于安装脚本完成的编译步骤(不检查更改日期)并删除了一些编译标志(它使用了许多重复项)。