Linker CUDA:LNK2005头文件中使用的_设备_函数出错

Linker CUDA:LNK2005头文件中使用的_设备_函数出错,linker,cuda,Linker,Cuda,我有一个在头文件中定义的设备函数。它出现在头文件中的原因是,它被全局内核使用,因为它是一个模板内核,所以需要出现在头文件中 当此头文件包含在2个或多个.cu文件中时,我在链接过程中遇到LNK2005错误: FooDevice.cu.obj:错误LNK2005:“int” __cdecl getCurThreadIdx(void)“(?getCurThreadIdx@@YAHXZ)已定义 在Main.cu.obj中 为什么会出现这种错误?如何修复它 以下是产生上述错误的示例代码: FooDevic

我有一个在头文件中定义的设备函数。它出现在头文件中的原因是,它被全局内核使用,因为它是一个模板内核,所以需要出现在头文件中

当此头文件包含在2个或多个.cu文件中时,我在链接过程中遇到LNK2005错误:

FooDevice.cu.obj:错误LNK2005:“int” __cdecl getCurThreadIdx(void)“(?getCurThreadIdx@@YAHXZ)已定义 在Main.cu.obj中

为什么会出现这种错误?如何修复它

以下是产生上述错误的示例代码:

FooDevice.h:

#ifndef FOO_DEVICE_H
#define FOO_DEVICE_H

__device__ int getCurThreadIdx()
{
    return ( ( blockIdx.x * blockDim.x ) + threadIdx.x );
}

template< typename T >
__global__ void fooKernel( const T* inArr, int num, T* outArr )
{
    const int threadNum = ( gridDim.x * blockDim.x );

    for ( int idx = getCurThreadIdx(); idx < num; idx += threadNum )
        outArr[ idx ] = inArr[ idx ];

    return;
}

__global__ void fooKernel2( const int* inArr, int num, int* outArr );

#endif // FOO_DEVICE_H
\ifndef FOO\u设备\u H
#定义FOO_设备
__设备\uuuu int getCurThreadIdx()
{
返回((blockIdx.x*blockDim.x)+threadIdx.x);
}
模板
__全局无效fooKernel(常量T*inArr、int-num、T*outArr)
{
const int threadNum=(gridDim.x*blockDim.x);
对于(int idx=getCurThreadIdx();idx
FooDevice.cu:

#include "FooDevice.h"

// One other kernel that uses getCurThreadIdx()
__global__ void fooKernel2( const int* inArr, int num, int* outArr )
{
    const int threadNum = ( gridDim.x * blockDim.x );

    for ( int idx = getCurThreadIdx(); idx < num; idx += threadNum )
        outArr[ idx ] = inArr[ idx ];

    return;
}
#包括“foodevidence.h”
//另一个使用getCurThreadIdx()的内核
__全局无效fooKernel2(常量int*inArr、int-num、int*outArr)
{
const int threadNum=(gridDim.x*blockDim.x);
对于(int idx=getCurThreadIdx();idx
Main.cu:

#include "FooDevice.h"

int main()
{
    int num             = 10;
    int* dInArr         = NULL;
    int* dOutArr        = NULL;
    const int arrSize   = num * sizeof( *dInArr );

    cudaMalloc( &dInArr, arrSize );
    cudaMalloc( &dOutArr, arrSize );

    // Using template kernel
    fooKernel<<< 10, 10 >>>( dInArr, num, dOutArr );

    return 0;
}
#包括“foodevidence.h”
int main()
{
int num=10;
int*dInArr=NULL;
int*doutar=NULL;
常量int arrSize=num*sizeof(*dInArr);
cudaMalloc(&dInArr,arrSize);
Cudamaloc(和Doutar,arrSize);
//使用模板内核
fooKernel>(dInArr、num、doutar);
返回0;
}

为什么会出现此错误

因为您已经在foodDevice.cu和Main.cu中定义了头,所以现在您有了同一函数的两个副本,链接器会检测到这一点

如何修复它

如果您在foo.h中定义了以下内容

template<typename T> __device__ T foo(T x)
{
    return x;
}
template\uuuuuu设备\uuuuuuut foo(tx)
{
返回x;
}
和两个.cu文件,它们都包含foo.h并包含对它的调用,例如

int x = foo<int>(1);
intx=foo(1);
然后可以强制foo()内联:

模板
内联设备(T x)
{
返回x;
}
并致电:

int x = foo<int>(1);
intx=foo(1);
这将阻止它被多次声明

函数模板是一个例外 一个定义规则,可能更多 它们的定义不止一个 不同的翻译单位。满满的 函数模板专门化是 不是一个模板,而是一个普通的 函数,因此需要使用内联 关键字不违反ODR,如果你想 将它们放入包含的头文件中 分为几个翻译单位

取自

另见:

我更改了您的代码,如下所示:

inline __device__ int getCurThreadIdx()
{
    return ( ( blockIdx.x * blockDim.x ) + threadIdx.x );
}

template< typename T >
__global__ void fooKernel( const T* inArr, int num, T* outArr )
{
    const int threadNum = ( gridDim.x * blockDim.x );

    for ( int idx = getCurThreadIdx(); idx < num; idx += threadNum )
        outArr[ idx ] = inArr[ idx ];

    return;
}
inline\uuuuu设备\uuuuuu int getCurThreadIdx()
{
返回((blockIdx.x*blockDim.x)+threadIdx.x);
}
模板
__全局无效fooKernel(常量T*inArr、int-num、T*outArr)
{
const int threadNum=(gridDim.x*blockDim.x);
对于(int idx=getCurThreadIdx();idx
现在它可以编译了。未内联getCurThreadIdx()的声明违反了一个定义规则。

它应该内联。您可以尝试添加
inline
关键字


也许你可以删除不必要的代码,创建一个简单的文本示例让我们看看?通常问题在于细节…

Ade Miller:我知道函数定义如何与C/C++代码一起工作。为什么CUDA设备函数有多个定义?它不是被吞食并嵌入内核吗?艾德·米勒:我已经用产生这个错误的示例代码更新了我的问题。你能修改你答案中的代码以适合我的问题代码吗?谢谢艾德·米勒:内联解决了这个问题!还有几个问题:像这样的简单设备函数不是由CUDA编译器内联的吗?另外,为什么要使用C/C++内联而不是CUDA forceinline?\uuuu forceinline或\uuuu inline也可以正确编译。至于你的另一个问题,我不能完全肯定。我怀疑这与fooKernel模板的扩展方式有关,但我不想进一步调查。您可以尝试打开中间编译器输出并查看生成的内容。不管怎样,我想你已经得到了答案。是的,但是模板化函数是全局的,所以它同时暴露在主机和设备代码中。我的假设是这是问题的一部分。CygnusX1:我已经在上面的问题中添加了示例代码。inline或forceinline限定符似乎都有效。但是,它们为什么起作用呢?为什么两者都能解决这个问题?一个是C/C++限定符,另一个是CUDA限定符!CUDA同时支持uuu inline和uuu forceinline(在host_defines.h中查找)。它们之所以起作用,是因为将getCurThreadIdx()内联到调用方法中,因此并非每次包含头时都定义getCurThreadIdx(),从而破坏了ODR。
inline __device__ int getCurThreadIdx()
{
    return ( ( blockIdx.x * blockDim.x ) + threadIdx.x );
}

template< typename T >
__global__ void fooKernel( const T* inArr, int num, T* outArr )
{
    const int threadNum = ( gridDim.x * blockDim.x );

    for ( int idx = getCurThreadIdx(); idx < num; idx += threadNum )
        outArr[ idx ] = inArr[ idx ];

    return;
}