如何使用 fortran (MKL) 将静态库中的函数打包到 .so 文件中

How do I pack a function from a static library into an .so file with fortran (MKL)

我正在尝试使用 MKL 在 Fortran 中的 blas 实现来加速优化例程。我需要将结果放在共享库中,以便可以从更大的脚本访问它。我可以在没有任何警告的情况下编译和 link 我的代码,但是生成的 .so 文件有一个对我试图调用的 blas 例程的未定义引用,即 dgemm.

fortran 代码的相关部分:

subroutine sumsquares(params, Mx, flen, glen, numr, numcols, g, lambda, res)
 implicit none
 integer, intent(in):: flen, glen, numr, numcols
 real(8), dimension(flen, numcols) :: params
 real(8), dimension(flen, numcols) :: g
 real(8), dimension(flen, numcols) :: gc
 real(8), dimension(flen, glen) :: Mx 

 gc = -g
 call dgemm('N', 'N', flen, glen, numcols, 1, Mx, flen, params, numcols, 1, gc,flen)
 return
end subroutine sumsquares

和对应的makefile

FC=ifort
LD_LIBRARY_PATH=/opt/intel/composerxe-2011.1.107/compiler/lib/intel64:/opt/intel/composerxe-2011.1.107/mkl/lib/intel64
LIBRARY_PATH=/opt/intel/composerxe-2011.1.107/compiler/lib/intel64:/opt/intel/composerxe-2011.1.107/mkl/lib/intel64
INCLUDE=/opt/intel/composerxe-2011.1.107/mkl/include
FPATH=/opt/intel/composerxe-2011.1.107/mkl/include
CPATH=/opt/intel/composerxe-2011.1.107/mkl/include
FFLAGS=-i8 -I$(MKLROOT)/include/intel64/ilp64 -I$(MKLROOT)/include
LDFLAGS= -shared -nofor-main -fPIC
LINKLIBS=-fPIC -shared -L$(MKLROOT)/lib/intel64 $(MKLROOT)/lib/intel64/libmkl_blas95_ilp64.a -lmkl_rt -lpthread -lm

sumsquares: sumsquares.f90
    $(FC) $(FFLAGS) -c -fPIC /opt/intel/composerxe-2011.1.107/mkl/include/blas.f90 -o blas95.o
    $(FC) $(FFLAGS) -nofor-main -c -fPIC sumsquares.f90
    $(FC) $(LDFLAGS) sumsquares.o $(LINKLIBS) sumsquares.o

正如我所说,我可以 运行 make 没有任何警告,但是当我 运行 nm sumsquares.so | grep dgemm 我看到 U dgemm_,当我尝试为了加载 .so 文件,我因段错误而崩溃(在调用不相关的子例程时,所以我认为它与此代码无关)。如何让 linker 将相关函数放入 so 文件?

更新

我通过在 R 中调用 dyn.load 来在 R 脚本中加载 so 文件,如下所示:

变体 1:

dyn.load("/home/me/path/sumsquares.so")

变体 2:

dyn.load("/opt/intel/composerxe-2011.1.107/mkl/lib/intel64/libmkl_rt.so")
dyn.load("/home/me/path/sumsquares.so")

两种变体导致相同的结果,但有段错误。

更新#2

我应该指出英特尔 MKL 静态库 编译时带有 -fPIC 标志,至少 according to the documentation。我显然不知道我在做什么,但据我所知,这样的事情应该是可能的。

由于blas广泛用于科学计算,我担心与不同版本的冲突。如果我在我的 so 文件中静态 linking,并将该 so 文件加载到使用不同 blas 实现的程序中,这会导致冲突吗,或者我的图书馆会很好玩吗?

How do I get the linker to put in the relevant function into the so file?

共享库不是这样工作的;您需要将 so 与您的其他文件一起发送(并设置适当的 RPATH),或者 link 到库的静态版本。

当你说:

when I try to load the .so file, I crash with a segfault

这听起来像是您要直接 dlopen() 或类似的东西;让动态(运行时)linker 完成它的工作。

如果库确实是静态的,则不能放入共享库。共享对象代码以不同的方式编译,因此它可以独立于位置工作。必须使用不用于静态库的标志,如 -fPIC

要么用dgemm编译你的BLAS作为一个动态库,然后在加载你的自定义库之前加载它(也许R dyn.load会自动加载依赖,我不知道,你可以尝试) 或者只将 DGEMM 的代码包含到您自己的库中并将所有内容一起编译成一个 .so.

不要忘记您必须使用 MKL Linking Advisor https://software.intel.com/content/www/us/en/develop/articles/intel-mkl-link-line-advisor.html 不要 link 使用 ILP64 库,除非您知道自己在做什么并且有充分的理由这样做。

此外,尽管大多数 MKL 都附带了使用 -fPIC 构建的静态库,但 LAPACK 和 BLAS 的 Fortran 95 接口却没有。包含接口的源文件,所以如果你想使用它们,你需要自己用 -fPIC 编译它们。它们位于 $MKLROOT/interfaces/blas95$MKLROOT/interfaces/lapack95