I'm having trouble using the CUDA Thrust library in MATLAB MEX code.
I have an example that runs fine externally, but if I compile and run it as a MEX file, it produces "missing symbol" errors at runtime.
It seems specific to the Thrust library. If instead of thrust::device_vector
I use cudaMalloc
with cudaMemcpy
or cublasSetVector
then everything is fine.
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
I can compile this from the command line (nvcc thrustDemo.cu
) and run the resulting executable just fine.
When I try to build this as a MATLAB MEX file (mexcuda thrustDemo.cu
from within MATLAB R2017a), it compiles and links just fine:
>> mexcuda thrustDemo.cu
Building with 'nvcc'.
MEX completed successfully.
But when I try to run it, I get the following error:
>> 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'.
This is pretty foreign to me; can somebody tell me what this means? These look like linker errors, but they're being generated at runtime. Also, I thought Thrust was a template library, so what is there to link to?
And finally, replacing thrust::device_vector
with cudaMalloc
and either cudaMemcpy
or cublasSetVector
works just fine. So for now I'm stuck with a bunch of cudaMalloc
in my code, which seems...distasteful. I'd really like to be able to use Thrust.
MATLAB R2017a
nvcc
V8.0.61, gcc
5.4.0, Ubuntu 16.04.2
NVidia driver 375.39, GTX 1060 graphics card (Compute Capability 6.1)
ldd
outputPer comments, I checked the dependencies of the MEX file using ldd thrustDemo.mexa64
:
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)
I tried looking for one of these missing symbols, and was able to find it:
$ nm -D /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv"
0000000000120be0 W _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv
So it seems that MATLAB must be looking in the wrong place.
It turns out this has nothing to do with Thrust but is rather an issue with MATLAB having its own version of the C++ standard libraries.
Thanks to @Navan and @talonmies for their helpful comments.
First, MATLAB is raising these errors when it's loading the MEX file. The MEX file has external dependencies, and MATLAB could not find them.
After checking these dependencies with the Linux utility ldd
, then using nm
to list the symbols define by these libraries, I found that the system version of the libstdc++
shared library actually contains these "missing symbols". Hence why the externally-compiled version works just fine.
The root problem, then, is that MATLAB ships with its own, older version of libstdc++
that is lacking these functions. Knowing the root cause, I found questions like these:
Version GLIBCXX_3.4.11 not found (required by buildW.mexglx)
which describe workarounds that were indeed successful for my problem.
In particular, I used LD_PRELOAD
when launching MATLAB to force MATLAB to use the system libstdc++
instead of its own copy:
$ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/local/MATLAB/R2017a/bin/matlab
It turns out that the folks at GCC are well aware of this incompatibility and 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.
To tell gcc
to use the older ABI, we just need to define _GLIBCXX_USE_CXX11_ABI
as 0
before we include any library headers, e.g. by passing a -D
option to the compiler:
-D_GLIBCXX_USE_CXX11_ABI=0
For the sake of completeness, I'll mention that my full mexcuda
call looks like this:
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);