C++ CUDA设备功能的多重定义

C++ CUDA设备功能的多重定义,c++,cuda,C++,Cuda,我试图编译一些函数,以便在主机代码和设备cuda代码中使用它们,但我得到了一个多定义链接错误。 我试图实现以下目标: 我有一个包含以下内容的CudaConfig.h文件 CudaConfig.h #ifdef __CUDACC__ #define CUDA_CALLABLE_DEVICE __device__ #define CUDA_CALLABLE_HOST __host__ #define CUDA_CALLABLE __host__ __device__ #else #define CU

我试图编译一些函数,以便在主机代码和设备cuda代码中使用它们,但我得到了一个多定义链接错误。 我试图实现以下目标:

我有一个包含以下内容的CudaConfig.h文件

CudaConfig.h

#ifdef __CUDACC__
#define CUDA_CALLABLE_DEVICE __device__
#define CUDA_CALLABLE_HOST __host__
#define CUDA_CALLABLE __host__ __device__
#else
#define CUDA_CALLABLE_DEVICE
#define CUDA_CALLABLE_HOST
#define CUDA_CALLABLE
#endif
在我的foo.h文件中,我有一些具有以下签名的函数

#include "CudaConfig.h"
struct Bar {Eigen::Vector3d v;};
CUDA_CALLABLE_DEVICE Eigen::Vector3d &foo(Bar &aBar);
我在foo.cpp和foo.cu文件中实现了它们

foo.cpp

#include "foo.h"

Eigen::Vector3d &foo(Bar &aBar) {aBar.v += {1,1,1}; return aBar.v;}
富库

#include "foo.h"

Eigen::Vector3d &foo(Bar &aBar) {aBar.v += {1,1,1}; return aBar.v;}

我需要在不同的文件中分离这两种实现,因为当您从
\uuu设备\uuu
函数使用它时,Eigen会禁用一些SIMD操作,因此出于性能原因,我不想在foo.cu文件中同时实现这两种操作


我是否应该直接在.h文件中实现该函数,将它们标记为内联,这样就不会出现多定义链接错误?由于Eigen禁用了
\uuuu设备\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>代码的SIMD,这难道不会使
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

rthoni@rthoni-lt1:~/projects/nvidia/test_device_host$ cat test.cu
extern "C" {
__device__ void test_device_fn()
{
}
}
rthoni@rthoni-lt1:~/projects/nvidia/test_device_host$ nvcc test.cu -c -o test_cu.o
rthoni@rthoni-lt1:~/projects/nvidia/test_device_host$ objdump -t test_cu.o 

test_cu.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 tmpxft_000004d9_00000000-5_test.cudafe1.cpp
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l     O .bss   0000000000000001 _ZL22__nv_inited_managed_rt
0000000000000008 l     O .bss   0000000000000008 _ZL32__nv_fatbinhandle_for_managed_rt
0000000000000000 l     F .text  0000000000000016 _ZL37__nv_save_fatbinhandle_for_managed_rtPPv
0000000000000010 l     O .bss   0000000000000008 _ZZL22____nv_dummy_param_refPvE5__ref
000000000000002f l     F .text  0000000000000016 _ZL22____nv_dummy_param_refPv
0000000000000000 l    d  __nv_module_id 0000000000000000 __nv_module_id
0000000000000000 l     O __nv_module_id 000000000000000f _ZL15__module_id_str
0000000000000018 l     O .bss   0000000000000008 _ZL20__cudaFatCubinHandle
0000000000000045 l     F .text  0000000000000022 _ZL26__cudaUnregisterBinaryUtilv
0000000000000067 l     F .text  000000000000001a _ZL32__nv_init_managed_rt_with_modulePPv
0000000000000000 l    d  .nv_fatbin 0000000000000000 .nv_fatbin
0000000000000000 l       .nv_fatbin 0000000000000000 fatbinData
0000000000000000 l    d  .nvFatBinSegment   0000000000000000 .nvFatBinSegment
0000000000000000 l     O .nvFatBinSegment   0000000000000018 _ZL15__fatDeviceText
0000000000000020 l     O .bss   0000000000000008 _ZZL31__nv_cudaEntityRegisterCallbackPPvE5__ref
0000000000000081 l     F .text  0000000000000026 _ZL31__nv_cudaEntityRegisterCallbackPPv
00000000000000a7 l     F .text  0000000000000045 _ZL24__sti____cudaRegisterAllv
0000000000000000 l    d  .init_array    0000000000000000 .init_array
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
0000000000000016 g     F .text  0000000000000019 test_device_fn
0000000000000000         *UND*  0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000         *UND*  0000000000000000 exit
0000000000000000         *UND*  0000000000000000 __cudaUnregisterFatBinary
0000000000000000         *UND*  0000000000000000 __cudaInitModule
0000000000000000         *UND*  0000000000000000 __cudaRegisterFatBinary
0000000000000000         *UND*  0000000000000000 atexit
如您所见,即使该函数仅标记为
\uuuu device\uuuu
nvcc
仍将在对象文件中为其生成一个符号

此行为是
nvcc
的一个bug(在我们的bug追踪器中为845649)

有3种方法可以消除此错误:

  • 让nvcc生成设备和主机代码
  • 更改编译
    cu
    文件的方式,只生成设备代码
  • \uuuu设备\uuuu
    函数包装在空命名空间中

在您的特定情况下,看起来您可以将其设置为
constexpr
未修饰的函数:

constexpr Eigen::Vector3d &foo(Bar &aBar) noexcept {aBar.v += {1,1,1}; return aBar.v;}
并使用
--expt-expr
调用
nvcc

--expt-relaxed-constexpr                   (-expt-relaxed-constexpr)       
        Experimental flag: Allow host code to invoke __device__ constexpr functions,
        and device code to invoke __host__ constexpr functions.Note that the behavior
        of this flag may change in future compiler releases.

这应该适用于设备和主机代码。

我怀疑您是否能做到这一点。该语言和工具链不允许在主机和设备代码中对给定函数进行不同的实现。您唯一的解决方案可能是使用模板标记来区分它们。我已经决定这样做。我已经把设备放在cuda名称空间中。感谢“需要在不同的文件中分离这两个实现,因为当您从一个“设备”功能使用它时,Eigen会禁用一些SIMD操作”。如果nvcc仅标记为
\uuuuuuuuuu设备
,nvcc如何构建
\uuuuuuuuu主机
功能?哦,对不起,我误读了标题中的宏。无论如何,nvcc仍然在主机代码中生成一个符号,这就是问题所在。我编辑了我的答案,以反映我从编译器团队得到的答案:这是nvcc中的一个bug。我正在编辑答案以添加这段信息。这个答案现在的措辞似乎仍然没有意义。为什么nvcc会为未声明的主机函数定义一个符号?是的,我删除了第一部分并添加了一些解释