использование MKL в cython завершается сбоем с неопределенным символом в общем объекте из MKL, но прямое выполнение в порядке

Я пишу физическое моделирование на C с использованием библиотеки Intel Math Kernel Library (MKL) и хочу вызвать его непосредственно из кода Python с помощью cython. Сама компиляция cython работает (программа работает без ошибок, если MKL не включен в пример), и если я скомпилирую свой код C непосредственно в gcc с помощью

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. Но если я сейчас попытаюсь скомпилировать тот же код с помощью 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), чтобы выполнить привязку MKL за пределами cython, а затем просто связать с ней, но после того, как я поиграл с этой библиотекой в ​​LD_LIBRARY_PATH, я получил такое же поведение снова.

Есть идеи, как правильно установить связь?

Я могу поменять используемую функцию cblas_ на другую (например, cblas_drot и еще несколько) и получить ту же ошибку.

Я прочитал несколько других вопросов (многие из них касаются MKL в anaconda, мой MKL устанавливается вручную в /opt, как показано выше), включая this, который пытается использовать MKL в java и получает ту же ошибку. Я могу воспроизвести те же результаты относительно операторов 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

(содержит вторую версию как команду, можно запустить так)

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

бар.с

#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)

person schetefan24    schedule 13.02.2021    source источник
comment
Важно передавать компоновщику пути и библиотеки в правильном порядке. Передача их как extra_link_args может работать не так, как ожидалось (хотя не уверен, что это так, но может быть). Лучше использовать аргументы library_dirs и libraries, см. docs.python .org/3/distutils/   -  person ead    schedule 14.02.2021
comment
@ead Я добавил вторую версию setup.py, используя указанные аргументы. Я не мог понять некоторые из них (например, -Wl,--no-as-needed). Можете ли вы подтвердить, что результат соответствует вашим ожиданиям? Это дает ту же ошибку (как я и ожидал, потому что ранее я также пробовал полностью внешнюю компиляцию в качестве общей библиотеки)   -  person schetefan24    schedule 14.02.2021


Ответы (1)


Я решил проблему, используя статическое связывание 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

Это соответствует шагам компиляции, выполняемым сценарием установки (без проверки дат изменений) и с удаленными некоторыми флагами компиляции (используется много дубликатов).

person schetefan24    schedule 15.02.2021