All-to-all-v¶
Grid all-to-all-v¶
-
template<grid_send_buffer SBuf, typename RBuf, is_execution_policy Exec, grid_compatible_output_layout Order = layout::unordered>
requires grid_recv_buffer<RBuf, Order> && (!std::is_same_v<Order, layout::ordered_by_source> || std::indirectly_copyable<std::ranges::iterator_t<SBuf>, std::ranges::iterator_t<RBuf>>)
kamping::v2::result<SBuf, RBuf> grid_alltoallv(SBuf &&sbuf, RBuf &&rbuf, grid_comm<Exec> const &grid, Order order = {})¶ All-to-all-v that routes the data through a grid of processes, combining messages on the way.
A flat
alltoallvsends up to p−1 messages per process. On agrid_commwith dimensions s_0 × … × s_{k−1}, the data is instead exchanged in k phases, one per dimension, with up to s_i messages each: Σ s_i messages per process, about 2√p on a two-dimensional grid. This pays off for many small messages on many processes, at the cost of forwarding each element up to k times.The call looks like
kamping::v2::alltoallv, with agrid_comminstead of a communicator:dstl::grid_comm grid{comm}; std::vector<int> recv; dstl::grid_alltoallv(data | views::with_counts(counts) | views::auto_displs, recv | views::resize, grid);
With the default
layout::unordered,rbufreceives the right elements, but grouped by routing path rather than by source rank. Withlayout::ordered_by_source, the result is identical to a flatalltoallv, including the per-source counts, which needs a variable-size receive buffer (seegrid_recv_buffer) and some extra local work.- Parameters:
order–layout::unordered(default) orlayout::ordered_by_source.
- Returns:
The send and receive buffer, as from
kamping::v2::alltoallv.
Defined in header
<dstl/grid_alltoallv.hpp>
-
template<is_execution_policy Exec = execution_policy::par>
class grid_comm¶ A communicator whose processes are arranged in a k-dimensional grid, for
grid_alltoallv.The grid dimensions s_0 × … × s_{k−1} (their product is the number of processes) are chosen by a factoring strategy, by default a balanced two-dimensional grid (
dims_create), or given explicitly. Each dimension has a subcommunicator connecting the processes that differ only in that coordinate. Ranks are numbered row-major: the last dimension groups consecutive ranks, e.g. the processes of a compute node.Execselects how the grid alltoallv uses threads (seeexecution_policy). The communicator accessors (rank(),size(), …) refer to the global communicator.Defined in header
<dstl/grid_comm.hpp>-
explicit grid_comm(kamping::v2::comm_view global)¶
(1) -
template<factoring Strategy>
grid_comm(kamping::v2::comm_view global, Strategy strategy)¶
(2) -
template<std::ranges::input_range R>
requires std::convertible_to<std::ranges::range_value_t<R>, std::size_t>
grid_comm(kamping::v2::comm_view global, R &&dims)¶
(3) (1) Build a grid over
globalusing the default factoring strategy (balanced 2-level grid).(2) Build a grid over
globalusing a factoring strategy.(3) Build a grid over
globalwith explicit dimensions.dimsis any input range of values convertible tostd::size_t; the product ofdimsmust equal the size ofglobal(a complete grid).
-
std::size_t num_dims() const noexcept¶
- Returns:
The number of grid dimensions k.
-
std::size_t dim_size(std::size_t i) const¶
- Returns:
The size s_i of dimension i.
-
kamping::v2::comm_view subcomm(std::size_t i) const¶
- Returns:
A non-owning view of the subcommunicator for dimension i (size s_i).
-
template<std::ranges::output_range<std::size_t> R>
std::ranges::borrowed_iterator_t<R> coords(int rank, R &&out) const¶
(1) -
std::vector<std::size_t> coords(int rank) const¶
(2) (1) Decompose a global rank into its mixed-radix coordinates (c_0 most significant), writing the
num_dims()coordinates into the output rangeout. For a sized range, asserts it holds exactlynum_dims()elements; for an unbounded sink (e.g.std::views::countedof a back-inserter) the caller is responsible for capacity.(2)
- Returns:
(1) The iterator one past the last written coordinate.
(2) The mixed-radix coordinates of a global rank as a vector.
-
template<std::ranges::input_range R>
requires std::convertible_to<std::ranges::range_value_t<R>, std::size_t>
int rank_of(R &&coords) const¶ Compose mixed-radix coordinates (c_0 most significant) back into a global rank.
coordsis any input range of exactlynum_dims()values yielding the coordinates in dimension order.
-
bool is_complete() const noexcept¶
- Returns:
Whether the product of the dimensions equals the number of processes. Always true: incomplete grids are rejected at construction.
-
std::size_t stride(std::size_t i) const¶
- Returns:
The row-major stride of dimension i (product of all later dimensions; 1 for i==k-1).
-
int rank() const¶
- Returns:
The rank of the calling process in this communicator.
-
int size() const¶
- Returns:
The number of processes in this communicator.
-
kamping::v2::group group() const¶
Extract the group associated with this communicator.
The returned
groupis owned by the caller.- Throws:
mpi_error– ifMPI_Comm_groupfails.
-
comm dup() const¶
Collective: duplicate this communicator (MPI_Comm_dup).
All processes in the communicator must call this collectively. Delegates to the free function
kamping::v2::dup.- Throws:
mpi_error– ifMPI_Comm_dupfails.
-
comm split(int color, int key = 0) const¶
Collective: split this communicator by color and key (MPI_Comm_split).
Processes with the same
colorform a sub-communicator ordered bykey. PassMPI_UNDEFINEDascolorto opt out — result isMPI_COMM_NULL. Delegates to the free functionkamping::v2::split.- Throws:
mpi_error– ifMPI_Comm_splitfails.
-
explicit grid_comm(kamping::v2::comm_view global)¶
-
template<typename SBuf>
concept grid_send_buffer¶ template<typename SBuf> concept grid_send_buffer = kamping::v2::send_buffer_v<SBuf> && std::ranges::contiguous_range<SBuf> && detail::grid_element<std::ranges::range_value_t<SBuf>>
A send buffer for
grid_alltoallv: a variable-size send buffer that is also a contiguous range, e.g.data | views::with_counts(counts) | views::auto_displs. Its elements must be default-initializable and copyable.Defined in header
<dstl/grid_alltoallv.hpp>
-
template<typename RBuf, typename Order>
concept grid_recv_buffer¶ template<typename RBuf, typename Order> concept grid_recv_buffer = std::ranges::contiguous_range<RBuf> && ((std::is_same_v<Order, layout::ordered_by_source> && kamping::v2::recv_buffer_v<RBuf>) || (!std::is_same_v<Order, layout::ordered_by_source> && kamping::v2::recv_buffer<RBuf>)) && detail::grid_element<std::ranges::range_value_t<RBuf>>
A receive buffer for
grid_alltoallvwith receive layoutOrder, a contiguous range of default-initializable, copyable elements. Withlayout::ordered_by_sourceit must be a variable-size receive buffer, which receives the per-source counts (e.g.views::auto_recv_v); withlayout::unorderedany receive buffer will do (e.g. a presizedstd::vector<T>orviews::resize).Send and receive element types may differ. With
layout::unordered, as for a plainMPI_Alltoallv, only their datatype signatures have to match; withlayout::ordered_by_source, the send elements must also be copyable to the receive elements.Defined in header
<dstl/grid_alltoallv.hpp>
Factoring the grid¶
-
template<typename T>
concept factoring¶ template<typename T> concept factoring = requires(T const& strategy, kamping::v2::comm_view global) { { strategy(global) } -> std::convertible_to<std::vector<std::size_t>>; }
A factoring strategy: a callable that maps the global communicator to the grid dimensions, e.g.
dims_create.Defined in header
<dstl/factoring.hpp>
-
struct dims_create¶
A balanced grid with
levelsdimensions (default 2), computed withMPI_Dims_create: the dimensions multiply to the number of processes and are as close to each other as possible.Defined in header
<dstl/factoring.hpp>
-
struct explicit_dims¶
Dimensions given by the caller, used as they are; their product must equal the number of processes.
Defined in header
<dstl/factoring.hpp>
-
struct topology_aware¶
One dimension per hardware level: a two-dimensional grid of nodes × processes per node, derived from
MPI_Comm_split_type(MPI_COMM_TYPE_SHARED). Falls back todims_create{2}if the nodes have different numbers of processes.Defined in header
<dstl/factoring.hpp>
Execution policies¶
-
struct seq¶
Sequential: one thread regroups the elements between phases and issues all MPI calls. Works with any MPI thread level.
Defined in header
<dstl/tags.hpp>
-
struct par¶
Parallel computation (the default): the elements are regrouped between phases with OpenMP, but MPI is called from a single thread. Requires at least
MPI_THREAD_FUNNELED. Without OpenMP, the regrouping is sequential.Defined in header
<dstl/tags.hpp>
Receive layouts¶
Multithreaded flat all-to-all-v¶
-
template<kamping::v2::send_buffer_v SBuf, kamping::v2::recv_buffer_v RBuf>
kamping::v2::result<SBuf, RBuf> alltoallv(SBuf &&sbuf, RBuf &&rbuf, thread_multiple_comm const &fc)¶ All-to-all-v that uses several threads for the communication itself, on a
dstl::thread_multiple_comm.The call looks like
kamping::v2::alltoallv, with athread_multiple_comminstead of a communicator. The data for each destination is split among thetthreads of the communicator, andtMPI_Alltoallvs run concurrently, one per thread, on their own duplicated communicators. The result is identical to a singlealltoallv. RequiresMPI_THREAD_MULTIPLE.dstl::thread_multiple_comm tmc{comm}; auto [_, recv] = dstl::alltoallv(data | views::with_counts(counts) | views::auto_displs, kamping::v2::auto_recv_v<int>(), tmc);
Defined in header
<dstl/flat_alltoallv.hpp>
-
class thread_multiple_comm¶
A communicator for
alltoallvwith concurrent communication from several threads: it ownstduplicates of the global communicator, one per thread. Its accessors (rank(),size(), …) refer to the global communicator.Defined in header
<dstl/thread_multiple_comm.hpp>-
explicit thread_multiple_comm(kamping::v2::comm_view global)¶
Duplicates
globalonce per thread (collective);globalitself is borrowed. Asserts that MPI was initialized withMPI_THREAD_MULTIPLE.The number of threads
tis the maximum offlat_max_threads()over all processes, since it can differ between them, e.g. with CPU affinity.
-
kamping::v2::comm_view global() const noexcept¶
- Returns:
A view of the underlying global communicator.
-
std::span<kamping::v2::comm const> thread_comms() const noexcept¶
- Returns:
The
tper-thread communicators used by the concurrent exchange.
-
int rank() const¶
- Returns:
The rank of the calling process in this communicator.
-
int size() const¶
- Returns:
The number of processes in this communicator.
-
kamping::v2::group group() const¶
Extract the group associated with this communicator.
The returned
groupis owned by the caller.- Throws:
mpi_error– ifMPI_Comm_groupfails.
-
comm dup() const¶
Collective: duplicate this communicator (MPI_Comm_dup).
All processes in the communicator must call this collectively. Delegates to the free function
kamping::v2::dup.- Throws:
mpi_error– ifMPI_Comm_dupfails.
-
comm split(int color, int key = 0) const¶
Collective: split this communicator by color and key (MPI_Comm_split).
Processes with the same
colorform a sub-communicator ordered bykey. PassMPI_UNDEFINEDascolorto opt out — result isMPI_COMM_NULL. Delegates to the free functionkamping::v2::split.- Throws:
mpi_error– ifMPI_Comm_splitfails.
-
explicit thread_multiple_comm(kamping::v2::comm_view global)¶
-
int flat_max_threads() noexcept¶
Number of OpenMP threads the thread_multiple exchange parallelizes over (1 without OpenMP).
Defined in header
<dstl/thread_multiple_comm.hpp>
Inspecting the setup¶
-
std::string as_string(kamping::v2::comm_view comm)¶
(1) -
template<is_execution_policy Exec>
std::string as_string(grid_comm<Exec> const &grid)¶
(2) (1) Collectively build a per-rank thread-count table for
comm(e.g.rank 3: 8 thread(s)).Note
Collective over
comm— every rank must call it; the returned string is identical on all ranks.(2) Collectively build the grid breakdown for
grid, annotated with each rank’s thread count (r(threads)). Gathers thread counts over the global communicator, then renders the structure.Note
Collective over the grid’s global communicator — every rank must call it; the result is identical on all ranks. For a communication-free structural dump use
std::format("{}", grid)instead.Defined in header
<dstl/visualization.hpp>
-
std::vector<int> gather_thread_counts(kamping::v2::comm_view comm)¶
Collectively gather every rank’s
local_thread_count()overcomm.Note
Collective over
comm— every rank must call it.- Returns:
A vector of size
comm.size()indexed by rank, identical on all ranks.
Defined in header
<dstl/visualization.hpp>
-
int local_thread_count() noexcept¶
- Returns:
The number of threads the calling rank actually runs with in an OpenMP parallel region, measured by entering one and reading
omp_get_num_threads()from inside it;1in a build without OpenMP. This reflects the real team size the runtime grants (e.g. after dynamic adjustment or affinity limits), unlikeomp_get_max_threads(), which is only the configured upper bound.
Defined in header
<dstl/visualization.hpp>