首页 > 解决方案 > 在 cython 中使用 MKL 失败,在来自 MKL 的共享对象中有未定义的符号,但直接执行没问题

问题描述

我正在使用 Intel Math Kernel Library (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.

我还尝试将我的程序(没有主程序)编译到一个共享库.soLD_LIBRARY_PATH

知道如何使链接正确吗?

我可以将使用过的cblas_功能换成不同的功能(例如cblas_drot,还有一些尝试过的功能)并得到相同的错误。

我已经阅读了很多其他问题(许多关于 anaconda 中的 MKL,我的 MKL 是手动安装的,/opt如上面的路径所示),包括这个试图在 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)

用过的文件

安装程序.py

(包含第二个版本作为命令,可以像这样运行)

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", ["foo.pyx"],
        include_dirs=[numpy.get_include(), "\"${MKLROOT}/include\""],
        libraries=["mkl_intel_lp64", "mkl_sequential", "mkl_core", "pthread", "m", "dl"],
        library_dirs=["${MKLROOT}/lib/intel64"],
        extra_compile_args=["-Wall", "-m64"],
        extra_link_args=["-Wl,--no-as-needed", ])
]

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

酒吧.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)

标签: cythonldintel-mklcythonize

解决方案


我已经通过使用 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

这对应于安装脚本完成的编译步骤(不检查更改日期)并删除了一些编译标志(它使用了许多重复项)。


推荐阅读