在可执行文件中链接 CUDA 文件时出现 cuSparse 函数的多个定义错误

Multiple definition errors of cuSparse functions while linking CUDA files in an executable

我的项目包含五个 CUDA 文件:main.cu jacobian_kernel.cu hermite_kernel.cu cuSolver_LU.cpp Utilities.cu,最后一个改编自 this GitHub repo,连同它的 Utilities.h header 文件;三个 header 是 args.h linear_solver.h Utilities.h.

nvcc 成功地编译了它们,但是在构建 trbdf2 可执行文件时它对我大喊大叫多个定义错误,例如:

nvcc -Xcompiler -fopenmp -o obj/trbdf2  obj/jacobian_kernel.o  obj/hermite_kernel.o  obj/utils.o  obj/cusolver_lu.o  obj/main.o -I/usr/local/cuda-8.0/targets/x86_64-linux/include -I../include -I/usr/local/cuda-8.0/samples/common/inc/ -L/usr/local/cuda-8.0/targets/x86_64-linux/lib -lgomp -lcublas -lcudart -lcusolver -lcusparse
obj/hermite_kernel.o: In function `vec_norminf(int, double const*)':
tmpxft_00006336_00000000-4_hermite_kernel.cudafe1.cpp:(.text+0xba1): multiple definition of `vec_norminf(int, double const*)'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xba1): first defined here
obj/hermite_kernel.o: In function `mat_norminf(int, int, double const*, int)':
tmpxft_00006336_00000000-4_hermite_kernel.cudafe1.cpp:(.text+0xc20): multiple definition of `mat_norminf(int, int, double const*, int)'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xc20): first defined here
...
obj/main.o: In function `second()':
tmpxft_00006385_00000000-4_main.cudafe1.cpp:(.text+0xf32): multiple definition of `second()'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xf32): first defined here
collect2: error: ld returned 1 exit status
Makefile:17: recipe for target 'obj/trbdf2' failed
make: *** [obj/trbdf2] Error 1

现在,我很确定我多次包含了 CUDA 工具包随附的 header helper_cusolver.h,它定义了函数 vec_norminfmat_norminf,等等。我猜不出如何重写我的 headers,它的开头如下:

args.h:

#if !defined(ARGS_H_)
#define ARGS_H_

#include <stdio.h> 
#include <stdlib.h> 
#include <iostream>
#include <math.h>
#include <assert.h>

#include <cuda.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>

#include "Utilities.h"
...
#endif

linear_solver.h:

#ifndef LINEAR_SOLVER_H_
#define LINEAR_SOLVER_H_

#include <cublas_v2.h>
#include <cusparse_v2.h>
#include <cusolverDn.h>

#include <helper_cusolver.h>
...
#endif

Utilities.h:

#ifndef UTILITIES_CUH
#define UTILITIES_CUH

#include "linear_solver.h"
...
#endif

此外,依赖项是:

jacobian_kernel.cu, hermite_kernel.cu -> args.h
cuSolver_LU.cpp -> args.h, linear_solver.h, Utilities.h
main.cu -> args.h, linear_solver.h, Utilities.h
Utilities.cu -> linear_solver.cu

此外,Utilities.cu初始指令与其他cuda文件开头的指令有点不同,但它是我项目中最近添加的,在添加之前我得到了同样的错误;无论如何,这里是:

#include "cuda_runtime.h"
#include <cuda.h>

#if defined(__CUDACC__) && (CUDA_VERSION >= 7000)
#include <cusolverDn.h>
#endif

#include <cublas_v2.h>

#include "Utilities.h"

长话短说,问题似乎是 helper_cusolver.h header 被包含了不止一次,尽管我什至在 linear_solver.h header的第一行;我已经尝试了 #pragma once 指令,nvcc 支持它,我什至检查了 helper_cusolver.

上的守卫

我是 C/C++ 的初学者,我真的不知道如何从这里开始。我试图一次取消注释大部分(显然是多个)#include 指令,但我一直收到相同的错误。

如果我应该包含其他信息,请告诉我。

EDIT 此外,当我用样本的 cudaCheckErrors 或 GitHub 中的 cusolveSafeCall 包装 CUDA and/or cuSolver 函数时 Utilities.cu,它们似乎没有被定义:

cuSolver_LU.cpp: In function ‘void linearSolverLU(cusolverDnHandle_t, int, const double*, int, const double*, double*)’:
cuSolver_LU.cpp:28:51: error: cannot convert ‘cudaError_t {aka cudaError}’ to ‘cusolverStatus_t’ for argument ‘1’ to ‘void cusolveSafeCall(cusolverStatus_t)’
     cusolveSafeCall(cudaMalloc(&info, sizeof(int)));

尽管正确包含 header Utilities.h。我认为这可能是找到错误指令的有用信息。

看来我需要清理 #include 指令。正如 Robert Crovella 所指出的,我从 linear_solver.h 中删除了 helper_cusolver.h,而是将其放入 cuSolver_LU.cpp,因为它是编译期间唯一需要它的文件,并且可能将其包含在更多文件导致多个定义错误;现在 cuSolver_LU.cpp 开始是这样的:

#include "args.h"
#include "linear_solver.h"
#include "Utilities.h"
#include <helper_cusolver.h>

extern "C" void cusolveSafeCall(cusolverStatus_t err);

...

此外,通过声明 cusolveSafeCall() 我还设法解决了包装函数的定义错误,我在编辑说明中指出了这一点。谢谢!

如果有多个文件需要 "helper_cusolver.h",则此解决方案将不起作用。我通过在该文件中的每个函数之前添加 "static" 来解决它。请注意 "helper_cuda.h" 中的函数有此 "static",这就是为什么它可以多次包含而不会出现问题的原因。

或者更好的是,请参阅 https://codeyarns.com/2010/08/26/c-static-function-in-header-file/