c++cmakecudaeigen

How to include and link against both kokkos and eigen


I am starting from a C++ codebase which depends upon Eigen, and I am attempting to see if it can be combined with the Kokkos library to add GPU functionality, so I need both of these to be found and linked against in the CMake file.

Additionally my Eigen dependency was already using BLAS and LAPACK.

If I include either of these dependencies in the target_link_libraries section, then all is well, however if I include both at once then the compilation fails with

In file included from /main.cpp:6:
In file included from /bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/Dense:7:
In file included from /bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/Eigenvalues:55:
/bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h:80:1: error: __host__ function 'compute' cannot overload __host__ __device__ function 'compute'
   80 | EIGEN_LAPACKE_EIG_SELFADJ(double,   double,                double, dsyev)
      | ^
/bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h:77:9: note: expanded from macro 'EIGEN_LAPACKE_EIG_SELFADJ'
   77 |         EIGEN_LAPACKE_EIG_SELFADJ_2(EIGTYPE, LAPACKE_TYPE, LAPACKE_RTYPE, LAPACKE_NAME, ColMajor )  \
      |         ^
/bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/src/Eigenvalues/SelfAdjointEigenSolver_LAPACKE.h:43:72: note: expanded from macro 'EIGEN_LAPACKE_EIG_SELFADJ_2'
   43 | SelfAdjointEigenSolver<Matrix<EIGTYPE, Dynamic, Dynamic, EIGCOLROW> >::compute(const EigenBase<InputType>& matrix, int options) \
      |                                                                        ^
/bask/apps/live/EL8-ice/software/Eigen/3.4.0-GCCcore-11.3.0/include/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h:216:29: note: previous declaration is here
  216 |     SelfAdjointEigenSolver& compute(const EigenBase<InputType>& matrix, int options = ComputeEigenvectors);
      |                             ^

This error seems to imply that Eigen is attempting to use device (GPU?) functionality.

I have a minimal reproduction of the code here, a single main.cpp file and the CMake script:

main.cpp:

#define lapack_complex_float std::complex<float>
#define lapack_complex_double std::complex<double>
#define EIGEN_USE_BLAS
#define EIGEN_USE_LAPACKE
#include <Eigen/Dense>

int main(void){
    Eigen::Matrix3d::Zero()
}

CMakeList.txt:

cmake_minimum_required (VERSION 3.8)

set(CMAKE_CXX_STANDARD 20)

project ("trial")

find_package(Kokkos REQUIRED)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
find_package(Eigen3 REQUIRED NO_MODULE)

add_executable(main "main.cpp")

target_link_libraries(
    main
    Kokkos::kokkos
    Eigen3::Eigen
    BLAS::BLAS
    LAPACK::LAPACK
    )

Note that this assumes Kokkos, BLAS and Eigen are already installed and findable.

It will work if I remove the link to Kokkos, however adding the Kokkos::kokkos link (even without invoking any Kokkos code) causes the compilation to fail.

FYI I was using:


Solution

  • This error seems to imply that Eigen is attempting to use device (GPU?) functionality.

    Eigen has some CUDA support in terms of marking their functions as __host__ __device__ when detecting a CUDA compiler so one can use them in device code, i.e. they do not provide actual kernels.


    Analysis

    As Kokkos with CUDA backend will compile .cpp files with a CUDA compiler, I played around a bit with the given code on cuda.godbolt.org using NVCC 11.8 which gives different warnings and errors (The errors can be avoided by passing --expt-relaxed-constexpr to NVCC.).

    Finally I tried using Clang as the CUDA compiler and I got the same errors reported in the question. I.e. Kokkos (or rather its CMake package) defaults to using Clang as the CUDA compiler instead of NVCC if one doesn't set CMAKE_CXX_COMPILER explicitly.

    I don't think Eigen's CUDA support is tested with Clang as the CUDA compiler and Clang handles __host__ and __device__ slightly differently to NVCC which might be the reason for this particular problem, i.e. this issue is not directly connected to Kokkos at all and it seems to be known at Eigen.

    Solution

    As OP does not seem to intend using Eigen in GPU code, I would recommend just turning off the Eigen CUDA support by defining EIGEN_NO_CUDA.

    I'm not sure if it is possible to combine Eigen's CUDA support with Kokkos' CUDA backend*, but if that is the aim, one has to get the Kokkos CMake package to use NVCC (or rather Kokkos' nvcc_wrapper) over Clang as its CUDA compiler:

    -DCMAKE_CXX_COMPILER=<Kokkos Install Directory>/bin/nvcc_wrapper
    

    as described in the Kokkos documentation.


    *: It doesn't really make sense to do so as one would lose some of the portability given by Kokkos, but I see no reason for it not to work with nvcc_wrapper and maybe even for HIP with some hacking.