Ecosystem adapters

Each adapter is a separate CMake component and target, enabled with the matching option (see Getting started). See GPUs and the HPC ecosystem for an introduction.

Kokkos

Target kamping::v2::kokkos, option KAMPING_V2_ENABLE_KOKKOS.

template<typename View>
auto kokkos(View &&view)

Makes a Kokkos::View a data buffer: kv | views::kokkos.

Contiguous views are passed to MPI directly, after fencing the execution space. Non-contiguous views are packed into a contiguous buffer with Kokkos::deep_copy on first access to their data, and unpacked after receiving. Rank-1 views can be resized for receiving: kv | views::kokkos | views::resize.

Defined in header <kamping/v2/ecosystem/kokkos.hpp>

template<typename T>
auto auto_kokkos_view(std::string const &label)
(1)
template<typename T>
auto auto_kokkos_view()
(2)

(1) Returns a resize_view wrapping an owning rank-1 kokkos_view for automatic-size receive. The underlying Kokkos::View is accessible via .underlying() on the result.

(2) Returns a resize_view wrapping an owning rank-1 kokkos_view for automatic-size receive. Template parameter is the element type, e.g. auto_kokkos_view<int>().

Defined in header <kamping/v2/ecosystem/kokkos.hpp>

template<typename T>
requires Kokkos::is_view_v<std::remove_reference_t<T>>
class kokkos_view

Wraps a Kokkos::View and presents it as an MPI data buffer.

Always stores an owned copy of the Kokkos::View handle (cheap: ref-counted pointer). If the wrapped view is already contiguous this wrapper does nothing extra.

Send path: mpi_count()/mpi_ptr() const lazily pack the wrapped view into a contiguous buffer on first access via deep_copy(). Recv path: mpi_resize_for_receive(n) resizes the wrapped rank-1 view and resets pack state; compose with | resize for automatic sizing via infer(). mpi_ptr() returns a mutable pointer; unwrap()/operator* trigger deep_copy back.

When built with KokkosComm (KAMPING_HAS_KOKKOS_COMM defined), the concept constraint and contiguous-buffer type are delegated to KokkosComm::Impl; otherwise a plain Kokkos::View with the same layout and memory space is used.

Defined in header <kamping/v2/ecosystem/kokkos.hpp>

void unwrap() const

Triggers deep copy back to the base view if needed without returning a reference. Equivalent to (void)**this but expresses intent at call sites that only care about the side effect (e.g. immediately followed by operator->).

view_type const &operator*() const
(1)
view_type &operator*()
(2)

(1) Dereference to the wrapped object, triggering deep copy back to the base view if needed.

(2) This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

view_type *operator->()
(1)
view_type const *operator->() const
(2)

(1) Arrow operator; triggers deep copy back to the base view if needed.

(2) This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Thrust

Target kamping::v2::thrust, option KAMPING_V2_ENABLE_THRUST.

template<typename T, typename Alloc>
struct buffer_traits<thrust::device_vector<T, Alloc>>

Specializes only ptr() — count and type fall through to the std::ranges fallbacks since thrust::device_vector models sized_range with a known value_type.

Defined in header <kamping/v2/ecosystem/thrust_buffer_traits.hpp>

template<typename Buf>
auto thrust(Buf &&buf)

Pipe adaptor: redirects mpi_ptr() to the raw Thrust device pointer, so CUDA-aware MPI reads and writes device memory directly: d_vec | kamping::v2::views::thrust.

Defined in header <kamping/v2/ecosystem/thrust.hpp>

SYCL

Target kamping::v2::sycl, option KAMPING_V2_ENABLE_SYCL.

template<sycl_accessor Accessor>
auto sycl(Accessor &&acc)
auto sycl(::sycl::interop_handle const &ih)
template<sycl_accessor Accessor>
auto sycl(Accessor &&acc, ::sycl::interop_handle const &ih)

Pipe adaptor: wraps a SYCL accessor as an MPI buffer.

Device accessor (inside a host_task) — pass the interop handle:

h.host_task([=](sycl::interop_handle ih) {
    kamping::v2::send(acc | kamping::v2::views::sycl(ih), 1, 0, world);
    kamping::v2::send(kamping::v2::views::sycl(acc, ih), 1, 0, world);  // equivalent
});

Host accessor — use as a zero-argument closure (no handle needed):

sycl::host_accessor hacc{buf, sycl::read_only};
kamping::v2::send(hacc | kamping::v2::views::sycl, 1, 0, world);

Defined in header <kamping/v2/ecosystem/sycl.hpp>

template<sycl_accessor Base>
class sycl_view

Wraps a SYCL accessor and exposes it as an MPI data buffer.

Two construction modes:

  • Device accessor (inside a host_task): construct with the accessor and sycl::interop_handle. mpi_ptr() extracts the device pointer via get_native_mem() for the active backend.

  • Host accessor: construct with the accessor only. mpi_ptr() uses get_pointer(), which returns the host-side contiguous buffer pointer.

mpi_count() and mpi_type() are forwarded from the accessor via view_interface.

Template parameters:
  • Base – A sycl::accessor or sycl::host_accessor specialization.

Defined in header <kamping/v2/ecosystem/sycl.hpp>

explicit sycl_view(Base base, sycl::interop_handle const &ih)
(1)
explicit sycl_view(Base base)
(2)

(1) Device accessor constructor. ih must outlive this view.

(2) Host accessor constructor.

template<typename Self = Derived>
requires has_materialize<decltype(std::declval<Self&>().base())>
void materialize()

Forwards eager realization to the wrapped base, so a deferred resizing view (resize_view / resize_v_view) can still be materialized when nested inside other view layers. Use the free function kamping::v2::materialize() rather than calling this directly.

template<typename Self = Derived>
requires deferred_recv_buf<std::remove_cvref_t<decltype(std::declval<Self&>().base())>>
void set_recv_count(std::ptrdiff_t n)

Forwards the single-count deferred protocol (set_recv_count) so a resize_view nested inside other view layers stays a deferred_recv_buf.

auto &underlying() &

Returns the container (or other buffer) at the bottom of a chain of views, e.g. the std::vector in std::vector<int>{} | views::resize.

Use it to get your container back from the result of an operation. On an rvalue, the container is moved out; on an lvalue, a reference to it is returned:

std::vector<int> data = kamping::v2::recv(std::vector<int>{} | views::resize, source, comm).underlying();
auto [_, recv] = kamping::v2::allgatherv(local, kamping::v2::auto_recv_v<int>(), comm);
std::vector<int>& all = recv.underlying();

Cereal

Target kamping::v2::cereal, option KAMPING_V2_ENABLE_CEREAL.

template<typename T>
auto serialize(T &&obj)

Range adaptor that wraps any object in a serialization_view for MPI transport. Pipe an lvalue or rvalue through it: obj | kamping::v2::views::serialize. Lvalues produce a non-owning view; rvalues produce an owning view.

Defined in header <kamping/v2/ecosystem/cereal.hpp>

template<typename T>
requires std::default_initializable<T>
auto deserialize()

Makes a receive buffer that deserializes into a new, default-constructed T.

The buffer sizes itself to the incoming message, like views::resize; the object is deserialized on first access through operator*:

auto received = kamping::v2::recv(kamping::v2::views::deserialize<MyType>(), source, comm);
MyType& result = *received;

Defined in header <kamping/v2/ecosystem/cereal.hpp>

template<typename T, typename Alloc = std::allocator<char>>
class serialization_view

Wraps an object and serializes/deserializes it with cereal for MPI transport.

Construct via obj | kamping::v2::views::serialize (non-owning, lvalue) or kamping::v2::views::deserialize<T>() (owning, default-constructed recv target).

Template parameters:
  • T – The wrapped type: a (possibly const) lvalue reference for non-owning views, or a value type for owning views.

  • Alloc – Allocator for the internal byte buffer (default: std::allocator<char>).

Send path — mpi_count() / mpi_ptr() const lazily serialize the wrapped object into _buffer on first access; the ostringstream result is moved (no copy) into _buffer. A const view satisfies send_buffer but not recv_buffer.

Recv path — set_recv_count(n) records the incoming byte count; mpi_ptr() (non-const) lazily resizes _buffer to that size so MPI can write into it directly; operator* / unwrap() then trigger lazy deserialization via a zero-copy membuf streambuf and clear _buffer to leave a deterministic empty state. Requires non-const T; a non-const view satisfies both send_buffer and recv_buffer.

Range semantics are intentionally omitted. Access the wrapped object via operator* or operator->; for ranges, dereference first: for (auto& x : *view) { … }.

Defined in header <kamping/v2/ecosystem/cereal.hpp>

explicit serialization_view(value_type &obj)
(1)
explicit serialization_view(value_type &&obj)
(2)

(1) Non-owning constructor: stores a pointer to the referenced object. Handles both T& and T const& (value_type may be const-qualified).

(2) Owning constructor: takes ownership of a moved object.

void unwrap() const

Triggers deserialization if needed without returning a reference. Equivalent to (void)**this but expresses intent at call sites that only care about the side effect (e.g. immediately followed by operator->).

value_type const &operator*() const
(1)
value_type &operator*()
(2)

(1) Dereference to the wrapped object, triggering deserialization if needed.

(2) This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

value_type *operator->()
(1)
value_type const *operator->() const
(2)

(1) Arrow operator; triggers deserialization if needed.

(2) This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

void set_recv_count(std::ptrdiff_t n)

Called by infer() with the number of bytes to receive. Requires non-const T: deserialization writes into the wrapped object.

void materialize()

Eagerly resizes _buffer to the _recv_count bytes recorded by set_recv_count(), so MPI can write into it. Idempotent. Called automatically by mpi_ptr(), or explicitly via kamping::v2::materialize() to realize the buffer before the MPI call. Does not deserialize — that happens on first read via unwrap() / operator*.