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

3
我在使用MATLAB MEX代码中的CUDA Thrust库时遇到了一些问题。我有一个在外部运行良好的示例,但是如果将其编译并作为MEX文件运行,则会在运行时产生“缺少符号”错误。这似乎是针对Thrust库的特定情况。如果我使用`cudaMalloc`和`cudaMemcpy`或`cublasSetVector`而不是`thrust :: device_vector`,那么一切都正常。下面是一个最小化的示例:
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内部运行),它能够成功编译和链接:

>> 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替换为cudaMalloccudaMemcpycublasSetVector也可以正常工作。因此,现在我的代码中有大量的cudaMalloc,这似乎不太好。我真的很想能够使用Thrust。

版本

MATLAB R2017a

nvcc V8.0.61,gcc 5.4.0,Ubuntu 16.04.2

NVidia驱动程序375.39,GTX 1060显卡(计算能力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似乎在错误的位置查找。

这不是运行时错误。错误发生在加载mex文件时。我不知道错误的原因。但是你应该能够使用像Linux中的ldd工具检查你的mex文件以检查依赖项。 - Navan
这是某种破损的C++/stdlib问题或主机编译器不匹配所导致的。涉及到的函数是std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const,与CUDA无关。 - talonmies
我明白了,错误输出现在更有意义了。我认为MATLAB更喜欢使用自己的libstdc++版本,这可能是根本原因。感谢你们两位的评论。 - KQS
1个回答

4
原来这与Thrust无关,而是MATLAB自己有其版本的C++标准库的问题。
感谢@Navan和@talonmies提供的有用评论。
解释错误
首先,当MATLAB加载MEX文件时,它会引发这些错误。 MEX文件具有外部依赖项,MATLAB找不到它们。
在使用Linux实用程序ldd检查这些依赖项之后,再使用nm列出这些库定义的符号后,我发现系统版本的libstdc ++共享库实际上包含这些“缺少的符号”。 因此,为什么外部编译版本可以正常工作。
解决问题
那么,根本问题在于MATLAB带有自己的旧版本的libstdc ++,缺少这些函数。 了解了根本原因后,我发现了像这样的问题:

如何告诉mex链接/usr/lib中的libstdc++.so.6而不是MATLAB目录中的文件?

版本GLIBCXX_3.4.11未找到(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的开发者已经很清楚这种不兼容性并在此处讨论

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

为了保持与链接到libstdc++的现有代码的向后兼容性,库的soname没有更改,并且旧实现仍与新实现并行支持。 ... _GLIBCXX_USE_CXX11_ABI宏(请参见宏)控制库头文件中的声明使用旧的还是新的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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接