Inference and deferred buffers¶
Before each operation, a kamping::v2 wrapper calls infer() with the operation’s tag, its buffers and the
communicator. For plain buffers it does nothing. For deferred buffers, which do not know their receive count(s)
yet, it obtains them: by probing the message, by a count exchange, or locally where the count is already determined.
See Receive buffers that size themselves.
Deferred buffers¶
-
template<typename T>
concept deferred_recv_buf¶ template<typename T> concept deferred_recv_buf = requires(T& t, std::ptrdiff_t n) { t.set_recv_count(n); }
Recv buffer whose size is not known upfront and is determined by infer(). The collective calls set_recv_count(n) after inferring n; the view then lazily resizes on first mpi_ptr() access.
Defined in header
<kamping/v2/views/concepts.hpp>
-
template<typename T>
concept deferred_recv_buf_v¶ template<typename T> concept deferred_recv_buf_v = has_mpi_counts_mutable<T> && has_commit_counts<T> && has_set_comm_size<T>
Recv buffer for variadic collectives whose per-rank counts are not known upfront. The infer() protocol:
set_comm_size(comm_size) — pre-allocate the counts buffer
MPI writes into mpi_counts() — fill per-rank counts directly
commit_counts() — signal counts are ready; invalidate cached state
Defined in header
<kamping/v2/views/concepts.hpp>
-
template<typename T>
concept deferred_send_buf_v¶ template<typename T> concept deferred_send_buf_v = send_buffer_v<T> && has_set_comm_size<T>
Send buffer for variadic collectives whose per-rank counts depend on the communicator size (e.g. a sparse or flattened source where ranks not present in the input must receive a count of 0). The infer() protocol calls set_comm_size(comm_size) before reading mpi_counts()/mpi_displs(), giving the buffer a chance to lay out its counts/displs/data across all comm_size ranks.
Unlike deferred_recv_buf_v, the counts are produced by the buffer rather than written by MPI, so no mutable mpi_counts()/commit_counts() is required.
Defined in header
<kamping/v2/views/concepts.hpp>
-
template<typename T>
concept has_monotonic_displs¶ template<typename T> concept has_monotonic_displs = requires(T const& t) { { t.displs_monotonic() } -> std::convertible_to<bool>; }
A buffer whose mpi_displs() are guaranteed to be monotonically non-decreasing (e.g. computed via exclusive_scan). When true, resize_v_view can use the O(1) tight bound displs.back()+counts.back() instead of the O(p) max formula.
Defined in header
<kamping/v2/views/concepts.hpp>
-
template<typename T>
void materialize(T &&buf)¶ Forces a deferred receive buffer to realize its backing storage now.
Deferred receive buffers (those built with
views::resize/views::resize_v/views::auto_recv/views::auto_recv_v) postpone resizing the underlying container and computing displacements until their data pointer is first accessed during the MPI call. Custom collectives sometimes need the buffer fully realized before the actual exchange — for instance to read back the final displacements in order to lay out a multi-phase transfer.materializemakes that an explicit, public operation instead of relying on the side effect of aptr()access.Contract: after
materialize(buf)returns,count(),ptr(),counts()anddispls()onbufyield final, stable values and the backing storage is sized. The operation is idempotent — calling it repeatedly, or letting the MPI wrapper trigger the lazy realization afterwards, is harmless.The
materialize()member is only invoked on buffers that are both materializable and an actual deferred receive buffer; on anything else (a plain, already-concrete buffer, or an unrelated type that merely happens to expose amaterialize()member) this is a no-op, so it is safe to call unconditionally in generic code.Propagates automatically through kamping view layers: an outer wrapper (with_type, GPU/serialization adaptors, …) around a deferred buffer forwards
materialize()— seeview_interface.hpp— so a nested deferred buffer is realized regardless of how many layers wrap it.- Parameters:
buf– The buffer to realize. Mutated in place.
Defined in header
<kamping/v2/views/concepts.hpp>
-
bool supports_matched_probe¶
Trait controlling whether
infer()uses matched probing (MPI_Mprobe / MPI_Mrecv) for a deferred recv buffer. Defaults totrue. Set tofalsefor buffer types whose MPI implementation does not support GPU-aware matched receives (e.g.thrust::device_vectorwith Intel MPI);infer()then falls back to a plain MPI_Probe followed by a regular MPI_Recv.Propagates automatically through kamping view layers — see
view_interface.hpp.To opt out a custom buffer type:
template <> inline constexpr bool kamping::v2::supports_matched_probe<MyGpuBuffer> = false;
Defined in header
<kamping/v2/views/concepts.hpp>
The infer() customization point¶
Add an overload of infer() for your own buffer types in their namespace; it is found by argument-dependent
lookup. The overloads below are the defaults.
-
template<recv_buffer RBuf>
auto infer(comm_op::recv, RBuf &rbuf, int source, int tag, MPI_Comm comm)¶
(1) -
template<recv_buffer RBuf>
void infer(comm_op::mrecv, RBuf &rbuf, v2::probe_result const &probe)¶
(2) -
template<send_recv_buffer SRBuf>
auto infer(comm_op::bcast, SRBuf &srbuf, int root, MPI_Comm comm)¶
(3) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::allgather, SBuf const &sbuf, RBuf &rbuf, MPI_Comm comm)¶
(4) -
template<send_buffer SBuf, recv_buffer_v RBuf>
void infer(comm_op::allgatherv, SBuf const &sbuf, RBuf &rbuf, MPI_Comm comm)¶
(5) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::alltoall, SBuf const &sbuf, RBuf &rbuf, MPI_Comm)¶
(6) -
template<send_buffer_v SBuf, recv_buffer_v RBuf>
void infer(comm_op::alltoallv, SBuf &sbuf, RBuf &rbuf, MPI_Comm comm)¶
(7) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::sendrecv, SBuf const &sbuf, RBuf &rbuf, int dest, int send_tag, int source, int recv_tag, MPI_Comm comm)¶
(8) -
template<send_recv_buffer SRBuf>
void infer(comm_op::sendrecv_replace, SRBuf&, int, int, int, int, MPI_Comm)¶
(9) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::reduce, SBuf const &sbuf, RBuf &rbuf, MPI_Op, int root, MPI_Comm comm)¶
(10) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::allreduce, SBuf const &sbuf, RBuf &rbuf, MPI_Op, MPI_Comm)¶
(11) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::gather, SBuf const &sbuf, RBuf &rbuf, int root, MPI_Comm comm)¶
(12) -
template<send_buffer SBuf, recv_buffer_v RBuf>
void infer(comm_op::gatherv, SBuf const &sbuf, RBuf &rbuf, int root, MPI_Comm comm)¶
(13) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::scatter, SBuf const &sbuf, RBuf &rbuf, int root, MPI_Comm comm)¶
(14) -
template<send_buffer_v SBuf, recv_buffer RBuf>
void infer(comm_op::scatterv, SBuf &sbuf, RBuf &rbuf, int root, MPI_Comm comm)¶
(15) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::scan, SBuf const &sbuf, RBuf &rbuf, MPI_Op, MPI_Comm)¶
(16) -
template<send_buffer SBuf, recv_buffer RBuf>
void infer(comm_op::exscan, SBuf const &sbuf, RBuf &rbuf, MPI_Op, MPI_Comm)¶
(17) (1) recv / irecv: probes the message (
MPI_Mprobe, orMPI_Probeif the buffer does not support matched probes) to size a deferred receive buffer. Returns the matchedMPI_Message, or the resolved{source, tag}after a plain probe, which the caller then receives.(2) mrecv / imrecv: the message has already been probed, so a deferred receive buffer is sized from the probed count.
(3) bcast: broadcasts root’s count to size a deferred buffer on the other ranks.
(4) allgather: sizes a deferred receive buffer to
comm_size * count(sbuf).(5) allgatherv: allgathers the send counts into the deferred counts of the receive buffer.
(6) alltoall: sizes a deferred receive buffer to
count(sbuf).(7) alltoallv: tells a deferred send buffer (e.g.
flatten_v_view) the communicator size so it can lay out its counts, then alltoalls the send counts into the deferred counts of the receive buffer.sbufis non-const for the former.(8) sendrecv / isendrecv: exchanges the send counts to size a deferred receive buffer.
(9) sendrecv_replace: nothing to infer for a buffer of known size.
A deferred buffer is rejected:
MPI_Sendrecv_replaceuses one count for both directions, so the buffer cannot be sized to the incoming message without changing what is sent. Buffer types that can handle this themselves, e.g. by exchanging additional metadata, can provide their own overload.(10) reduce: sizes a deferred receive buffer on root to
count(sbuf).(11) allreduce: sizes a deferred receive buffer to
count(sbuf).(12) gather: sizes a deferred receive buffer on root to
comm_size * count(sbuf).(13) gatherv: gathers the send counts into the deferred counts of the receive buffer on root.
(14) scatter: broadcasts root’s per-rank count to size a deferred receive buffer.
(15) scatterv: tells a deferred send buffer on root the communicator size, then scatters root’s send counts to size a deferred receive buffer.
sbufis non-const for the former; non-root ranks passnull_buf_v.(16) scan: sizes a deferred receive buffer to
count(sbuf).(17) exscan: sizes a deferred receive buffer to
count(sbuf).Defined in header
<kamping/v2/infer.hpp>