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

时间:2017-04-26 20:10:12

标签: matlab mex thrust

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

我有一个外部运行良好的示例,但如果我编译并将其作为MEX文件运行,它会生成"缺少符号"运行时的错误。

它似乎特定于Thrust库。如果不是thrust::device_vector而是cudaMalloccudaMemcpycublasSetVector,那么一切都很好。

最低示例

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文件(MATLAB R2017a中的mexcuda thrustDemo.cu)时,它编译并链接就好了:

>> 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'.

这对我来说很陌生;谁能告诉我这意味着什么?这些看起来像链接器错误,但它们是在运行时生成的。另外,我认为Thrust是一个模板库,那么链接到什么呢?

最后,将thrust::device_vector替换为cudaMalloc,将cudaMemcpycublasSetVector替换为正常。所以现在我在我的代码中遇到了一堆cudaMalloc,这似乎......令人反感。我真的很想能够使用Thrust。

版本

MATLAB R2017a

nvcc V8.0.61,gcc 5.4.0,Ubuntu 16.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必须在错误的地方寻找。

1 个答案:

答案 0 :(得分:4)

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

感谢@Navan和@talonmies的有用评论。

解释错误

首先,MATLAB在加载MEX文件时会引发这些错误。 MEX文件具有外部依赖性,MATLAB无法找到它们。

使用Linux实用程序ldd检查这些依赖关系后,使用nm列出这些库定义的符号,我发现libstdc++共享库的系统版本实际上包含这些&#34;缺少符号&#34;。因此,为什么外部编译版本工作得很好。

解决问题

然后,根本问题是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

  

在GCC 5.1版本中,libstdc ++引入了一个新的库ABI,其中包括std :: string和std :: list的新实现。这些更改对于符合2011 C ++标准是必要的,该标准禁止写入时写入字符串并要求列表跟踪其大小。

     

为了保持与libstdc ++链接的现有代码的向后兼容性,库的soname没有改变,旧的实现仍然与新的实现并行支持。   ...   _GLIBCXX_USE_CXX11_ABI宏(请参阅宏)控制库头中的声明是使用旧的还是新的ABI。

要告诉gcc使用较早的ABI,我们只需要在{1}}之前将_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);