为什么直接从共享库调用CUDA内核时会出现分段错误?

时间:2016-01-28 22:56:45

标签: cuda cmake

我编写的方式(请参阅问题标题),但我得到了分段错误。那么我,CMake还是CUDA不支持来自共享库的直接内核调用?解决方案不一定是CMake

进一步详情:

我有以下文件结构:

testKernel.hpp

__global__ void kernelTest( float x );
void callKernel( float x );

testKernel.cu

#include "testKernel.hpp"

__global__ void kernelTest( float x ) {}
void callKernel( float x ) { kernelTest<<<1,1>>>( x ); }

useKernel.cu

#include <cstdio>
#include "testKernel.hpp"

int main( void )
{
    kernelTest<<<1,1>>>( 3.0f );
    //callKernel( 3.0f );
    printf("OK\n");
    return 0;
}

的CMakeLists.txt

cmake_minimum_required(VERSION 3.3.1)
project(testKernelCall)
find_package(CUDA REQUIRED)

cuda_add_library( ${PROJECT_NAME} SHARED testKernel.cu testKernel.hpp )
target_link_libraries( ${PROJECT_NAME} ${CUDA_LIBRARIES} )

cuda_add_executable("useKernel" useKernel.cu)
target_link_libraries("useKernel" ${PROJECT_NAME})

使用以下命令编译并运行:

cmake .; make && ./useKernel

导致分段错误。与gdb的回溯是:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff75726bd in cudart::configData::addArgument(void const*, unsigned long, unsigned long) ()
   from ./libtestKernelCall.so
(gdb) bt
#0  0x00007ffff75726bd in cudart::configData::addArgument(void const*, unsigned long, unsigned long) ()
   from ./libtestKernelCall.so
#1  0x00007ffff7562eb7 in cudart::cudaApiSetupArgument(void const*, unsigned long, unsigned long) ()
   from ./libtestKernelCall.so
#2  0x00007ffff7591ca2 in cudaSetupArgument ()
   from ./libtestKernelCall.so
#3  0x00007ffff7556125 in __device_stub__Z10kernelTestf (__par0=3)
    at /tmp/tmpxft_00003900_00000000-4_testKernel.cudafe1.stub.c:7
#4  0x00007ffff755616c in kernelTest (__cuda_0=3) at ./testKernel.cu:2
#5  0x000000000040280e in main () at ./useKernel.cu:6

经过测试(表示段错误出现在这些设置中):

  • 设置1

    • cmake 3.4.1
    • CUDA 7.0.27
    • g ++ 4.9.2
    • 的Debian
  • 设置2

    • cmake 3.3.1
    • CUDA 6.5.14
    • g ++ 4.7.1

有两种方法可以解决此错误:

  • 在CMakeList.txt
  • 中将SHARED更改为STATIC
  • 使用包装函数callKernel而不是直接调用内核

我真的不知道如何在没有CMake的情况下构建CUDA共享库。我知道如何构建一个CUDA静态库,但是这种情况似乎与CMake一起使用,所以没有CMake我没有测试它。

以下是我使用make VERBOSE=1获得的相关CMake命令。在可能的情况下,我将绝对路径更改为相对路径,但我不确定所有这些库路径。将这些命令放在一个文件中并获取该文件正确编译共享库和程序并“正确”导致分段错误。我还添加了command,因为对我来说,nvcc使用`-ccbin``选项别名。

make.sh

command nvcc "./testKernel.cu" -c -o "./testKernel.cu.o" -ccbin /usr/bin/cc -m64 -DtestKernelCall_EXPORTS -Xcompiler ,\"-fPIC\",\"-g\" -DNVCC -I/opt/cuda-7.0/include -I/opt/cuda-7.0/include
/usr/bin/c++  -fPIC   -shared -Wl,-soname,libtestKernelCall.so -o libtestKernelCall.so ./testKernel.cu.o /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so
command nvcc "./useKernel.cu" -c -o "./useKernel.cu.o" -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -DNVCC -I/opt/cuda-7.0/include -I/opt/cuda-7.0/include
/usr/bin/c++ ./useKernel.cu.o  -o useKernel -rdynamic /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so libtestKernelCall.so /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so -Wl,-rpath,"."

3 个答案:

答案 0 :(得分:3)

如果我将nvcc开关添加到每个 nvcc命令,您的代码将使用普通的-cudart shared命令(而不是CMake)为我编译并正确运行。这是一个完整的序列:

$ cat testKernel.hpp
__global__ void kernelTest( float x );
void callKernel( float x );
$ cat testKernel.cu
#include "testKernel.hpp"

__global__ void kernelTest( float x ) {}
void callKernel( float x ) { kernelTest<<<1,1>>>( x ); }
$ cat useKernel.cu
#include <cstdio>
#include "testKernel.hpp"

int main( void )
{
    kernelTest<<<1,1>>>( 3.0f );
    //callKernel( 3.0f );
    cudaDeviceSynchronize();
    printf("OK\n");
    return 0;
}
$ nvcc -shared -cudart shared -o test.so -Xcompiler -fPIC testKernel.cu
$ nvcc -cudart shared -o test test.so useKernel.cu
$ cuda-memcheck ./test
========= CUDA-MEMCHECK
OK
========= ERROR SUMMARY: 0 errors
$

如果我在上述-cudart shared命令的 上省略nvcc,那么编译仍将继续进行,但在执行时我将看到上述的seg错误。在Fedora 20上使用CUDA 7.5进行测试。

根据我的测试,关于你的CMake设置,有必要链接对抗共享cudart。因此,将-cudart shared添加到-c命令(这是编译命令)是不够的。如果我不清楚,请注意。我上面的“编译”命令正在执行 编译并且在每一步都进行链接。)

nvcc关联时,正确的切换为-cudart shared。但是,您的make.sh表示最终链接由主机c ++编译器完成:

command nvcc "./testKernel.cu" -c -o "./testKernel.cu.o" -ccbin /usr/bin/cc -m64 -DtestKernelCall_EXPORTS -Xcompiler ,\"-fPIC\",\"-g\" -DNVCC -I/opt/cuda-7.0/include -I/opt/cuda-7.0/include
/usr/bin/c++  -fPIC   -shared -Wl,-soname,libtestKernelCall.so -o libtestKernelCall.so ./testKernel.cu.o /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so
command nvcc "./useKernel.cu" -c -o "./useKernel.cu.o" -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -DNVCC -I/opt/cuda-7.0/include -I/opt/cuda-7.0/include
/usr/bin/c++ ./useKernel.cu.o  -o useKernel -rdynamic /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so libtestKernelCall.so /opt/cuda-7.0/lib64/libcudart_static.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so /usr/lib/x86_64-linux-gnu/libdl.so -Wl,-rpath,"."

在这种情况下,您不希望链接:

/opt/cuda-7.0/lib64/libcudart_static.a

但反对libcudart.so

/opt/cuda-7.0/lib64/libcudart.so

如果您直接编辑make.sh,则需要在已显示的/usr/bin/c++命令行的两个中进行更改。例如,如果我要修改我已经提交的编译序列以反映您使用主机c ++编译器进行链接,它将如下所示:

$ nvcc -c -Xcompiler -fPIC testKernel.cu                     
$ g++ -fPIC -shared -o test.so -L/usr/local/cuda/lib64 -lcudart testKernel.o
$ nvcc -c useKernel.cu
$ g++ -o test -L/usr/local/cuda/lib64 -lcudart test.so useKernel.o
$ cuda-memcheck ./test
========= CUDA-MEMCHECK
OK
========= ERROR SUMMARY: 0 errors
$

答案 1 :(得分:1)

In [33]: stacked = np.random.randint(0,10,(1000,1000,100)) In [34]: %timeit stacked.shape[-1] - stacked[...,::-1].argmax(-1) - 1 1 loop, best of 3: 281 ms per loop In [35]: %timeit (stacked == stacked.max(-1,keepdims=1)).cumsum(-1).argmax(-1) 1 loop, best of 3: 659 ms per loop 之前放set(CUDA_USE_STATIC_CUDA_RUNTIME OFF)将完成相当于find_package(CUDA REQUIRED)的工作

答案 2 :(得分:0)

这是Robert Crovella's answer的扩展。 我使用以下CMakeLists.txt并且效果很好。

cmake_minimum_required(VERSION 3.8)

project(cmake_and_cuda LANGUAGES CXX CUDA)

add_library(my_cu SHARED testKernel.cu testKernel.h)
target_link_libraries(my_cu PRIVATE cudart) #MUST!!
set(CMAKE_CUDA_FLAGS "-shared -cudart shared -Xcompiler -fPIC"
CACHE STRING "Use libcudart.dylib" FORCE)
set(CMAKE_MACOSX_RPATH FALSE)

add_executable(app useKernel.cu)
target_link_libraries(app PRIVATE cudart) #MUST!!
target_link_libraries(app PRIVATE my_cu)

我使用CMake 3.10,我的操作系统是OS X EI Capitan 10.11.6。 对我来说,如果我没有将CMAKE_MACOSX_RPATH设置为FALSE,我将收到Library not loaded错误。也许对你来说没有必要。

请注意,由于CMake 3.8FindCUDA被取代,因此设置CUDA_USE_STATIC_CUDA_RUNTIME不会产生任何影响。 您可以查看this postthis document了解详情。 此外,this post提供了关于如何在CUDA之后处理CMake 3.8的一个很好的示例。