0

My project consists of five CUDA files: main.cu jacobian_kernel.cu hermite_kernel.cu cuSolver_LU.cpp Utilities.cu, the last of which is adapted from this GitHub repo, together with its Utilities.h header file; the three headers are args.h linear_solver.h Utilities.h.

nvcc compiles them succesfully, but while building the trbdf2 executable it yells at me multiple definitions errors like:

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

Now, I'm quite sure that I'm including multiple times the header helper_cusolver.h which is provided with the CUDA Toolkit and which defines the functions vec_norminf, mat_norminf, and the like. I couldn't guess how to rewrite my headers, which begin as follows:

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

Moreover, the dependencies are:

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

In addition, the Utilities.cu initial directives are a bit different from those at the beginning of the other cuda files, but it is a recent addition to my project, and I got the same error before adding it; anyway, here it is:

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

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

#include <cublas_v2.h>

#include "Utilities.h"

Long story short, the issue appears to be that the helper_cusolver.h header is being included more than once, though I've even put some header guard within the linear_solver.h header's first lines; I've tried the #pragma once directive, which is supported by nvcc, and I even checked the guard on the helper_cusolver.

I'm a beginner in C/C++ and I really don't know how to follow from here. I tried to uncomment most of the (apparently multiple) #include directives, once at a time but I keep getting the same error.

Let me know if I should include other piece of info.

EDIT In addition, when I wrap the CUDA and/or cuSolver functions with the cudaCheckErrors of the samples or the GitHub's cusolveSafeCall in Utilities.cu, they appear not to be defined:

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)));

though the header Utilities.h is correctly included. I thought this could be useful info in order to find the incorrect directive.

Eugenio
  • 244
  • 2
  • 13
  • 1
    include guards and `#pragma once` won't help when you include the same thing in 2 different compilation units. In which modules do you actually need things from `helper_cusolver.h`? – Robert Crovella Nov 29 '16 at 00:22
  • `cuSolver_LU` is the only one that needs it; btw it is a code adapted from the CUDA samples' cuSolver – Eugenio Nov 29 '16 at 08:37
  • so rearrange your header files so that `helper_cusolver.h` only gets included in that module. This could be as simple as removing `helper_cusolver.h` from any and all header files, and just put the include statement for it: `#include ` directly in the `cuSolver_LU.cpp` file. – Robert Crovella Nov 29 '16 at 08:42
  • it worked! that was so simple. Still, I thought the headers were included correctly. still can't get the "safe calls" working though, maybe I'll get them a new answer. – Eugenio Nov 29 '16 at 09:02
  • 1
    Please add a short answer explaining your solution. – talonmies Nov 29 '16 at 11:50

2 Answers2

2

It seems like I need to clean up #include directives. As pointed out by Robert Crovella I removed helper_cusolver.h from linear_solver.h and instead I've put it in cuSolver_LU.cpp, as it is the only file that needs it during compilation, and probably including it in more files caused multiple definition errors; now cuSolver_LU.cpp begins like this:

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

extern "C" void cusolveSafeCall(cusolverStatus_t err);

...

Moreover, by declaring cusolveSafeCall() I managed as well to solve definition errors on wrapper functions, which I pointed out in the edit note. Thanks!

Eugenio
  • 244
  • 2
  • 13
0

This solution will not work if there is more than one file that needs "helper_cusolver.h" ..I solved it by adding "static" preceding each function in this file. Note that functions in "helper_cuda.h" have this "static", which is why it can be included multiple times without problems.

Or better still, see https://codeyarns.com/2010/08/26/c-static-function-in-header-file/