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

Posted

技术标签:

【中文标题】在 MATLAB MEX 文件中使用 Thrust 的运行时链接器错误【英文标题】:Runtime linker error using Thrust in MATLAB MEX file 【发布时间】:2017-04-26 20:10:12 【问题描述】:

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

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

它似乎是 Thrust 库特有的。如果我使用 cudaMalloccudaMemcpycublasSetVector 而不是 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 文件(来自 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 是一个模板库,那有什么可以链接的呢?

最后,用cudaMalloccudaMemcpycublasSetVector 替换thrust::device_vector 就可以了。所以现在我在我的代码中被一堆cudaMalloc 困住了,这似乎......令人反感。我真的很想能够使用 Thrust。

版本

MATLAB R2017a

nvcc V8.0.61、gcc 5.4.0、Ubuntu 16.04.2

NVidia 驱动程序 375.39,GTX 1060 显卡(计算能力 6.1)

更新:ldd 输出

每个 cmets,我使用 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 文件以检查依赖关系。 这是某种损坏的 C++/stdlib 问题或主机编译器不匹配。涉及的函数是std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;::c_str() const,与CUDA无关 我明白了;错误输出现在更有意义了。我认为 MATLAB 更喜欢使用自己的 libstdc++ 版本,这可能是根本原因。谢谢你们的cmets。 【参考方案1】:

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

感谢 @Navan 和 @talonmies 提供帮助的 cmets。

解释错误

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

用Linux实用程序ldd检查这些依赖关系,然后使用nm列出这些库定义的符号后,我发现libstdc++共享库的系统版本实际上包含这些“缺失符号”。因此,为什么外部编译的版本可以正常工作。

解决问题

因此,根本问题是 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++ 标准,这些更改是必要的,该标准禁止 Copy-On-Write 字符串并要求列表跟踪其大小。

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

【讨论】:

以上是关于在 MATLAB MEX 文件中使用 Thrust 的运行时链接器错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Matlab 中使用 OpenCV:mex 找不到头文件

为啥这段代码(在 Matlab 的 MEX 文件中使用 OpenMP)给出不同的结果?

防止 MEX 文件在 MATLAB 中崩溃

在 MATLAB 中使用 MEX 文件访问存储在元胞数组中的矩阵

Mex 文件执行中的错误,Matlab 窗口

在 Windows 上使用 GFortran 在 Matlab 中创建 Mex 文件