将 ctypes 与使用英特尔 fortran 编译器编译的 fortran dll 一起使用并链接到英特尔的 mkl 库时缺少 Dll 依赖项

Dll dependencies missing when using ctypes with fortran dll compiled with intel fortran compiler and linking to intel's mkl library

我在windows下。 mkl_example.f中的堡垒运行代码:

    subroutine matmultmkl(M1, M2, M3, M, N, K) bind(c, name='matmultmkl')
        !DEC$ ATTRIBUTES DLLEXPORT :: matmultmkl
        use iso_c_binding, only: c_float, c_int
    
        integer(c_int),intent(in) :: M, N, K
        real(c_float), intent(in) :: M1(M, N), M2(N, K)
        real(c_float), intent(out):: M3(M, K)

        CALL DGEMM('N','N',M,K,N,1.,M1,M,M2,N,0.,M3,M)
    
    end subroutine 

我使用以下批处理文件用命令行编译它(在我 运行 compilervars.bat 之前的 cmd windows 中):

@Echo off

setlocal ENABLEDELAYEDEXPANSION
SET "IFORT_INITIAL_FLAGS=-c -fpp"
SET "IFORT_OPTIMIZATION_FLAGS=/O3"

ifort %IFORT_OPTIMIZATION_FLAGS% %IFORT_INITIAL_FLAGS% /I"C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\mkl\include" -o mkl_example.obj mkl_example.f
ifort -dll -o mylib.dll mkl_example.obj /link /LIBPATH:"C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.4.311\windows\mkl\lib\intel64_win" mkl_intel_lp64.lib mkl_intel_thread.lib mkl_core.lib libiomp5md.lib 

然后我 运行 遵循 python 脚本 mkl_example.py :

from ctypes import *
import time

import os
os.add_dll_directory("C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020.4.311/windows/mkl/lib/intel64_win")
os.add_dll_directory("C:/Program Files (x86)/IntelSWTools/compilers_and_libraries_2020.4.311/windows/compiler/lib/intel64_win")

import numpy as np

mylib = CDLL('./mylib.dll')

mylib.matmultmkl.argtypes = [ POINTER(c_float), 
                                POINTER(c_float), 
                                POINTER(c_float),
                                POINTER(c_int),
                                POINTER(c_int),
                                POINTER(c_int) ]
mylib.matmultmkl.restype = None
M=1000
N=1000
K=1000

a = np.empty((M,N), dtype=c_float)
b = np.empty((N,K), dtype=c_float)
c = np.empty((M,K), dtype=c_float)

a[:] = np.random.rand(M,N)
b[:] = np.random.rand(N,K)

# Fortran mkl call
start = time.time()
mylib.matmultmkl( a.ctypes.data_as(POINTER(c_float)), 
                    b.ctypes.data_as(POINTER(c_float)), 
                    c.ctypes.data_as(POINTER(c_float)), 
                    c_int(M), c_int(N), c_int(K) )
stop = time.time()
print(f"Fortran mkl \t {stop - start}s")

输出为:

Traceback (most recent call last):
  File "path\to\mkl_example.py", line 14, in <module>
    mylib = CDLL('./mylib.dll')
  File "C:\PYTHON.8.2_64bits\lib\ctypes\__init__.py", line 373, in __init__
    self._handle = _dlopen(self._name, mode)
FileNotFoundError: path\to\mylib.dll' (or one of its dependencies). Try using 
the full path with constructor syntax.

如果您删除所有 mkl 内容并自己编写一个函数,mylib.dll 会在 运行 时间被 python 找到。因此,在我 link 到 mkl 的情况下,消息并不意味着即使 mylib.dll 在正确的位置,由于某些奇怪的原因在 运行 时间找不到它,但确实没有找到它的依赖项之一。

除了找到 所有 依赖项并将它们放在 fort运行 dll 和 python 文件。但如果这是 唯一的 方式,也许我宁愿放弃。

在 python 脚本中添加到 DLL 搜索路径的路径用于包含编译器和 MKL 的静态库和导入库的目录,而不是运行时 DLL。

(英特尔编译器的安装通常会将编译器运行时安装在 Common files 目录中,并将其添加到系统搜索路径 - 因此编译器运行时 DLL 很可能会通过该路径找到 - 但这确实不适用于 MKL 运行​​时 DLL。)

os.add_dll_directory 调用中使用正确的目录。它将类似于(您需要检查您的安装)C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_xxxx\windows\redist\intel64_win\mkl。与版本无关的符号链接目录也可能更合适,具体取决于您的需要。

(在没有安装编译器的情况下部署到另一台机器将需要一个关于如何部署依赖项的策略,但这是一个更大的话题。)