在 MATLAB MEX 文件中使用 Thrust 时出现运行时链接器错误

Runtime linker error using Thrust in MATLAB MEX file

我在 MATLAB MEX 代码中使用 CUDA Thrust 库时遇到问题。

我有一个示例 运行 在外部没问题,但是如果我编译 运行 它作为 MEX 文件,它会在 运行 时产生 "missing symbol" 错误.

它似乎特定于 Thrust 库。如果我使用 cudaMalloc 而不是 thrust::device_vectorcudaMemcpycublasSetVector 那么一切都很好。

最小示例

thrustDemo.cu:

#ifdef MATLAB_MEX_FILE
    #include "mex.h"
    #include "gpu/mxGPUArray.h"
#endif
#include <thrust/device_vector.h>
#include <vector>

void thrustDemo() {
    std::vector<double> foo(65536, 3.14);
    thrust::device_vector<double> device_foo(foo);
}

#ifdef MATLAB_MEX_FILE
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray const *prhs[]) {
    thrustDemo();
}
#else
int main(void) { thrustDemo(); }
#endif

问题

我可以从命令行 (nvcc thrustDemo.cu) 编译它,运行 生成的可执行文件就好了。

当我尝试将其构建为 MATLAB MEX 文件(mexcuda thrustDemo.cu 从 MATLAB R2017a 中)时,它编译并且 link 很好:

>> mexcuda thrustDemo.cu
Building with 'nvcc'.
MEX completed successfully.

但是当我尝试 运行 它时,出现以下错误:

>> thrustDemo()
Invalid MEX-file '/home/kqs/thrustDemo.mexa64':
Missing symbol '_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5emptyEv' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt12length_errorC1EPKc' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt13runtime_errorC2EPKc' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEpLEPKc' required by '/home/kqs/thrustDemo.mexa64'
Missing symbol '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEpLERKS4_' required by '/home/kqs/thrustDemo.mexa64'.

这对我来说很陌生;有人可以告诉我这是什么意思吗?这些看起来像 linker 错误,但它们是在 运行 时生成的。另外,我以为Thrust是一个模板库,所以link有什么用?

最后,将 thrust::device_vector 替换为 cudaMalloc 并且 cudaMemcpycublasSetVector 都可以正常工作。所以现在我的代码中有一堆 cudaMalloc,这看起来……令人反感。我真的很想能够使用 Thrust。

版本

MATLAB R2017a

nvccV8.0.61,gcc5.4.0,Ubuntu16.04.2

NVidia驱动375.39,GTX 1060显卡(Compute Capability 6.1)

更新:ldd输出

根据评论,我使用 ldd thrustDemo.mexa64:

检查了 MEX 文件的依赖项
linux-vdso.so.1 =>  (0x00007ffdd35ea000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f097eccf000)
libcudart.so.8.0 => /usr/local/cuda-8.0/targets/x86_64-linux/lib/libcudart.so.8.0 (0x00007f097ea69000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f097e852000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f097e489000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f097e180000)
/lib64/ld-linux-x86-64.so.2 (0x0000562df178c000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f097df7b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f097dd5e000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f097db56000)

我试着寻找这些缺失的符号之一,并找到了它:

$ nm -D /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv"
0000000000120be0 W _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv

看来 MATLAB 一定找错了地方。

事实证明这与 Thrust 无关,而是 MATLAB 有自己的 C++ 标准库版本的问题。

感谢@Navan 和@talonmies 的宝贵意见。

解释错误

首先,MATLAB 在加载 MEX 文件时引发了这些错误。 MEX 文件有外部依赖项,MATLAB 找不到它们。

用Linux实用工具ldd检查了这些依赖关系,然后用nm列出了这些库定义的符号,发现[=15的系统版本=] 共享库实际上包含这些 "missing symbols"。这就是为什么外部编译的版本可以正常工作的原因。

解决问题

那么,根本问题是 MATLAB 附带了自己的 libstdc++ 旧版本,该版本缺少这些函数。知道了根本原因,我发现了这样的问题:

How to tell mex to link with the libstdc++.so.6 in /usr/lib instead of the one in the MATLAB directory?

Version GLIBCXX_3.4.11 not found (required by buildW.mexglx)

其中描述了对我的问题确实成功的解决方法。

特别是,我在启动 MATLAB 时使用了 LD_PRELOAD 来强制 MATLAB 使用系统 libstdc++ 而不是它自己的副本:

$ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/local/MATLAB/R2017a/bin/matlab

更新:更好的解决方案

事实证明,GCC 的人们很清楚这种不兼容性,discuss it here:

In the GCC 5.1 release libstdc++ introduced a new library ABI that includes new implementations of std::string and std::list. These changes were necessary to conform to the 2011 C++ standard which forbids Copy-On-Write strings and requires lists to keep track of their size.

In order to maintain backwards compatibility for existing code linked to libstdc++ the library's soname has not changed and the old implementations are still supported in parallel with the new ones. ... The _GLIBCXX_USE_CXX11_ABI macro (see Macros) controls whether the declarations in the library headers use the old or new ABI.

要告诉 gcc 使用旧的 ABI,我们只需要在包含任何库头之前将 _GLIBCXX_USE_CXX11_ABI 定义为 0,例如通过将 -D 选项传递给编译器:

-D_GLIBCXX_USE_CXX11_ABI=0

为了完整起见,我会提到我的完整 mexcuda 调用如下所示:

nvcc_opts = [...
    '-gencode=arch=compute_30,code=sm_30 ' ...
    '-gencode=arch=compute_50,code=sm_50 ' ...
    '-gencode=arch=compute_60,code=sm_60 ' ...
    '-std=c++11 ' ...
    '-D_GLIBCXX_USE_CXX11_ABI=0 '   % MATLAB's libstdc++ uses old library ABI
    ];
mexcuda_opts = {
    '-lcublas'                      % Link to cuBLAS
    '-lmwlapack'                    % Link to LAPACK
    '-lcufft'                       % Link to cuFFT
    ['NVCCFLAGS="' nvcc_opts '"']
    '-L/usr/local/cuda/lib64'       % Location of CUDA libraries
    };
mexcuda(mexcuda_opts{:}, src_file);