如何使用 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
。
我正在尝试使用 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
。