分离库的主机端版本和CUDA设备端版本

问题描述 投票:0回答:2

我有一个带有某些__host__ __device__函数的库。我还有一个#ifdef __CUDACC__ gadget,可确保常规C ++编译器看不到__host__ __device__,因此可以编译这些功能。

现在,我想在普通的C ++静态库文件(在Linux中为.a)中使用库函数的已编译主机端版本-我甚至希望该库在CUDA不可用时可以编译;我想将编译后的设备端版本放在单独的静态库中。

我想我几乎在那里,但是由于链接错误而卡住了。这是此类库的玩具资源,测试程序(调用函数的设备端版本和主机端版本)以及我使用的构建命令。

我怎么了?


  • [my_lib.hpp(库标题):
#ifdef __CUDACC__
__host__ __device__
#endif
void foo(int*x, int* y);
int bar();
  • my_lib.cu(库来源):
#include "my_lib.hpp"

#ifdef __CUDACC__
__host__ __device__
#endif
void foo(int*x, int* y)  { *x = *y; }

int bar() { return 5; }
  • main.cu(测试程序):
#include "my_lib.hpp"

__global__ void my_kernel() {
  int z { 78 };
  int w { 90 };
  foo(&z,&w);
}

int main() {
  int z { 123 };
  int w { 456 };
  foo(&z,&w);
  my_kernel<<<1,1>>>();
  cudaDeviceSynchronize();
  cudaDeviceReset();
}

我的构建命令:

c++ -c -x c++ -o my_lib-noncuda.o my_lib.cu
ar qc my_lib-noncuda.a my_lib-noncuda.o
ranlib my_lib-noncuda.a
nvcc -dc -o my_lib-cuda.o my_lib.cu
ar qc my_lib-cuda.a my_lib-cuda.o
ranlib my_lib-cuda.a
nvcc -dc -o main.rdc.o main.cu
nvcc -dlink -o main.o main.rdc.o my_lib-cuda.a
c++ -o main main.o my_lib-noncuda.a -lcudart

以及我得到的错误-最后,链接命令:

/usr/bin/ld: main.o: in function `__cudaRegisterLinkedBinary_39_tmpxft_00003f88_00000000_6_main_cpp1_ii_e7ab3416':
link.stub:(.text+0x5a): undefined reference to `__fatbinwrap_39_tmpxft_00003f88_00000000_6_main_cpp1_ii_e7ab3416'
/usr/bin/ld: main.o: in function `__cudaRegisterLinkedBinary_41_tmpxft_00003f69_00000000_6_my_lib_cpp1_ii_ab44b3f6':
link.stub:(.text+0xaa): undefined reference to `__fatbinwrap_41_tmpxft_00003f69_00000000_6_my_lib_cpp1_ii_ab44b3f6'
collect2: error: ld returned 1 exit status

注意:

  • 我在Devuan GNU / Linux上使用CUDA 10.1和g ++ 9.2.1。
  • 这是对已删除问题的“跟进”; @talonmies评论说,我最好确切地显示我的所作所为;这使问题有所改变。
  • 与某些问题:this one
c++ cuda linker static-libraries unresolved-external
2个回答
2
投票

tl; dr:如果将main()与内核调用分开,则拆分库设置可以工作。

让我们将您的示例修改为我认为您实际使用的情况。修改将main()放入要由.cpp编译的g++文件中,并将CUDA代码放入要由.cu编译的单独的nvcc文件中。这对于使您的两库设置工作非常重要。这是合理的,因为“ [主要包含要求单独编译和链接的CUDA内核”是nvcc编译模型的特例。

重组后的代码:

main.cu

include "my_lib.hpp"

__global__ void my_kernel() {
  int z { 78 };
  int w { 90 };
  foo(&z,&w);
}

int cudamain()
{
  my_kernel<<<1,1>>>();
  return 0;
}

main.cpp

#include <cuda_runtime_api.h>
#include "my_lib.hpp"

extern int cudamain();

int main() {
  int z { 123 };
  int w { 456 };
  foo(&z,&w);
  cudamain();
  cudaDeviceSynchronize();
  cudaDeviceReset();
}

所有其他文件仍保留在问题中。

构建程序所需的命令现在为:

c++ -c -x c++ -o my_lib-noncuda.o my_lib.cu
ar qc my_lib-noncuda.a my_lib-noncuda.o
ranlib my_lib-noncuda.a

nvcc -std=c++11 -dc -o my_lib-cuda.rdc.o my_lib.cu
ar qc my_lib-cuda.a my_lib-cuda.rdc.o
ranlib my_lib-cuda.a

# Until this line - identical to what you have tried in your question

nvcc -std=c++11 -c -rdc=true main.cu -o main.cu.o 
nvcc -dlink -o main.o main.cu.o my_lib-cuda.a

c++ -std=c++11 -o main main.cpp main.o main.cu.o -I/path/to/cuda/include \
    -L/path/to/cuda/lib64 my_lib-cuda.a my_lib-noncuda.a -lcudart -lcudadevrt

要记住的重要事项是,主机端组件需要在构建中继续进行。因此,您必须将CUDA主机代码的nvcc输出传递给主链接,并且还必须将CUDA侧库添加到主链接。否则,将缺少对代码的主机端运行时API支持。另请注意,必须链接设备运行时库才能使此工作生效。


0
投票

这是创建两个库的方法,一个仅包含CUDA设备功能,另一个仅包含主机功能。您可以省略“复杂的” #if#ifndef防护。但是,您的库my_lib-cuda.a中也将包含“非CUDA代码”。

有关其他问题,请参见@talonmies社区Wiki答案,或参考我已经在评论中发布的链接:https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/-“高级用法:使用其他链接器”一节。

my_lib.cu

#include "my_lib.hpp"

#ifdef __CUDA_ARCH__
__device__
#endif
#if (defined __CUDA_ARCH__) || (not defined __CUDACC__)
void foo(int*x, int* y)  { *x = *y; }
#endif

#ifndef __CUDACC__
int bar() { return 5; }
#endif

库的构建过程保持不变。

[构建CUDA程序:(通过仅使用nvcc而不是c++进行简化,或者来看看@talonmies社区Wiki答案)

nvcc -dc main.cu -o main.o
nvcc main.o my_lib-cuda.a my_lib-nocuda.a -o main

如果如上所述也省略了my_lib-nocuda.a中的#if#ifndef,则可以省略my_lib.cu的链接。

[构建C ++程序:(假设#ifdef __CUDACC__中的CUDA代码周围有main.cu防护)

c++ -x c++ -c main.cu -o main.o
c++ main.o my_lib-nocuda.a -o main
© www.soinside.com 2019 - 2024. All rights reserved.