Add a backend extension#
The extensions/ tree (and the corresponding public headers under
include/ginkgo/extensions/) holds user-facing libraries that depend
on third-party packages and bridge them with Ginkgo. They are not
“backends” in the kernel-dispatch sense — those live under cuda/,
hip/, omp/, dpcpp/.
Shipped extensions:
Extension |
Shape |
Public header |
|---|---|---|
Kokkos |
Header-only |
|
nlohmann JSON config |
Header-only |
|
yaml-cpp YAML config |
Header-only |
|
cuDSS direct solver |
Compiled library ( |
|
An extension can be header-only (Kokkos / JSON / YAML — pure
bridging headers that consumers #include) or a compiled library
target (cuDSS — wraps a vendor library as a LinOp, distributed as
its own Ginkgo::<name> library alongside Ginkgo::ginkgo). The
right shape depends on what the extension does.
What an extension provides#
Every extension, regardless of shape:
Lives under
namespace gko::ext::<name>(e.g.gko::ext::kokkos,gko::ext::cuda::solver). Internal details go in adetailsub-namespace.Has a public header tree under
include/ginkgo/extensions/<name>/. Header-only extensions usually expose a single umbrella headerextensions/<name>.hppthat pulls in the sub-headers.Optionally has a
config.hpp.intemplate processed byconfigure_fileso the extension can see Ginkgo’s compile-time switches.
Header-only extensions stop there. Compiled-library extensions additionally have:
One or more
.cppfiles underextensions/<area>/(e.g.extensions/cuda/solver/cudss.cpp).A library target produced by
add_library(...)in the relevantextensions/<area>/CMakeLists.txt, conventionally aliased asGinkgo::<name>.
Anatomy of the Kokkos extension (header-only)#
Umbrella header:
// include/ginkgo/extensions/kokkos.hpp
#include <ginkgo/extensions/kokkos/config.hpp>
#include <ginkgo/extensions/kokkos/spaces.hpp>
#include <ginkgo/extensions/kokkos/types.hpp>
The two substantive sub-headers do the bridging:
kokkos/spaces.hpp— definescompatible_space<MemorySpace, ExecType>, astd::integral_constant<bool, ...>that answers “can this Kokkos memory space be safely accessed from this Ginkgo executor?”. Used bymap_executorto refuse mismatched pairs at compile time.kokkos/types.hpp— definesvalue_type<T>::type(Kokkos::complex<T>forstd::complex<T>, identity otherwise) plus helpers that wrap agko::array<T>ormatrix::Dense<T>storage region as a non-owning KokkosView.
The config.hpp.in is processed by Ginkgo’s top-level CMakeLists:
configure_file(
${Ginkgo_SOURCE_DIR}/include/ginkgo/extensions/kokkos/config.hpp.in
${Ginkgo_BINARY_DIR}/include/ginkgo/extensions/kokkos/config.hpp
@ONLY
)
It substitutes GINKGO_BUILD_CUDA / GINKGO_BUILD_OMP / … into the
generated config.hpp so the Kokkos spaces header can guard
backend-specific bridges behind the right macros.
Anatomy of the cuDSS extension (compiled library)#
cuDSS wraps NVIDIA’s sparse direct solver as
gko::ext::cuda::solver::Cudss<V, I> — an ordinary LinOp with the
standard factory/parse scaffolding. Layout:
extensions/cuda/CMakeLists.txt ← find_package + add_library
extensions/cuda/solver/cudss.cpp ← implementation
include/ginkgo/extensions/cuda/solver/cudss.hpp ← public LinOp class
The CMake target is built only when the prerequisites are present:
# extensions/cuda/CMakeLists.txt
if(NOT GINKGO_BUILD_CUDA)
return()
endif()
if(NOT cudss_FOUND)
return()
endif()
add_library(ginkgo_cudss solver/cudss.cpp)
add_library(Ginkgo::ginkgo_cudss ALIAS ginkgo_cudss)
target_link_libraries(ginkgo_cudss PUBLIC ginkgo)
target_link_libraries(ginkgo_cudss PRIVATE cudss CUDA::cudart)
ginkgo_default_includes(ginkgo_cudss)
ginkgo_install_library(ginkgo_cudss)
The vendor dependency is discovered at the extensions/ top level:
# extensions/CMakeLists.txt
if(GINKGO_BUILD_CUDA)
find_package(cudss 0.7.1 CONFIG)
endif()
add_subdirectory(cuda)
Notes on this pattern:
The library links publicly to
ginkgo(so consumers get the public Ginkgo headers transitively) and privately to the vendor package and CUDA runtime (so consumers don’t have to discover them).The
Ginkgo::ginkgo_cudssalias is what users link against.ginkgo_default_includes+ginkgo_install_librarygive the extension the same install layout and include-path setup as the core Ginkgo target.
The wrapped class itself is a regular LinOp — Cudss inherits from
EnableLinOp<Cudss> and has the standard parameters_type / Factory
/ parse triplet (see Add a new solver for the
scaffolding). The cuDSS handles and factorization state are private
members, opaque to the public class.
How extensions plug into the build#
The top-level Ginkgo CMakeLists.txt calls
add_subdirectory(extensions) after the core build. The
extensions/CMakeLists.txt is small: it does the vendor-package
discovery shared across sub-extensions, walks into the
compiled-library subdirectories (e.g. cuda/), and conditionally
walks into test/ when GINKGO_BUILD_TESTS=ON:
# extensions/CMakeLists.txt
if(GINKGO_BUILD_CUDA)
find_package(cudss 0.7.1 CONFIG)
endif()
add_subdirectory(cuda)
if(GINKGO_BUILD_TESTS)
add_subdirectory(test)
endif()
What gets built depends on the extension:
Header-only extensions contribute nothing to the build — consumers just
#includethe header. Their tests live underextensions/test/<name>/and pull the third-party dependency viafind_package(withFetchContentfallback for JSON / YAML).Compiled-library extensions add a library target under
extensions/<area>/CMakeLists.txt, conditional on the vendor package being found. The build silently skips the target when the vendor dependency is missing (cuDSS prints an informationalSTATUSmessage and returns).
Adding a new extension#
For a header-only extension <myext>:
include/ginkgo/extensions/<myext>.hpp ← umbrella include
include/ginkgo/extensions/<myext>/types.hpp ← per-concern headers
include/ginkgo/extensions/<myext>/spaces.hpp
include/ginkgo/extensions/<myext>/config.hpp.in ← optional, if you need
Ginkgo build-flag knowledge
extensions/test/<myext>/CMakeLists.txt ← finds your third-party dep
extensions/test/<myext>/<test1>.cpp ← tests live here
CMake wiring:
If
<myext>needs a generatedconfig.hpp, add aconfigure_fileblock to the top-levelCMakeLists.txtnext to the Kokkos one.Append your test subdirectory to
extensions/test/CMakeLists.txt.In
extensions/test/<myext>/CMakeLists.txt, callfind_package(<MyExtDep> REQUIRED)(with aFetchContentfallback if appropriate) and linkGinkgo::ginkgoplus the third-party target.
For a compiled-library extension <myext> (vendor LinOp wrapper):
include/ginkgo/extensions/<area>/<myext>.hpp ← public LinOp class
extensions/<area>/<myext>.cpp ← implementation
extensions/<area>/CMakeLists.txt ← add_library + linking
extensions/test/<area>/<myext>_test.cpp ← tests
CMake wiring:
find_package(<MyVendor> ...)inextensions/CMakeLists.txt(so every compiled sub-extension underextensions/<area>/can see whether the dependency is present).add_subdirectory(<area>)fromextensions/CMakeLists.txt.In
extensions/<area>/CMakeLists.txt, early-return when the required Ginkgo backend isn’t enabled and when the vendor package isn’t found. Thenadd_library(ginkgo_<myext> <myext>.cpp),add_library(Ginkgo::ginkgo_<myext> ALIAS ginkgo_<myext>), public link toginkgo, private link to the vendor target, andginkgo_default_includes+ginkgo_install_library.
The class itself is built like any other LinOp — see Add a new
solver or Add a new
preconditioner for the factory / parse
scaffolding.
Vendor-LinOp wrappers: extensions vs core#
Vendor library wrappers can also live in core/ if they belong to a
shipped Ginkgo type, rather than in extensions/:
In
core/— when the vendor path is one implementation among several for a type that Ginkgo already exposes. Example:solver::LowerTrs/solver::UpperTrsroute triangular solves through cuSPARSE on the CUDA executor and through Ginkgo’s own kernels on others;factorization::Choleskyhas an optional vendor path. The vendor calls live incuda/solver/lower_trs_kernels.cuetc., gated by the executor type.In
extensions/— when the type is only the vendor wrapper and would not exist without it. Example:gko::ext::cuda::solver::Cudsshas no non-cuDSS implementation, so it’s its own extension library that consumers opt into.
See also
Add a new solver — LinOp + Factory + parse scaffolding that any vendor-backed solver also needs.
Add a new preconditioner — same scaffolding, no workspace.
Configure via JSON — what the JSON config extension unlocks for the runtime registry.