Getting started¶
Adding KaMPIng-v2 to your project¶
KaMPIng-v2 is header-only. It needs a C++20 compiler, CMake 3.25 or newer and an MPI installation; it is tested with
MPICH, Open MPI and Intel MPI, using GCC and Clang. The simplest way to use it is FetchContent:
include(FetchContent)
FetchContent_Declare(
kamping-v2
GIT_REPOSITORY https://github.com/kamping-site/kamping-v2.git
GIT_TAG main
)
FetchContent_MakeAvailable(kamping-v2)
target_link_libraries(my_app PRIVATE kamping::v2)
To use an ecosystem adapter, set its option before FetchContent_MakeAvailable, e.g.
set(KAMPING_V2_ENABLE_KOKKOS ON), and link its target.
As an installed package¶
cmake -B build -DCMAKE_BUILD_TYPE=Release -DKAMPING_V2_ENABLE_CEREAL=ON
cmake --install build --prefix /path/to/prefix
find_package(kamping-v2 REQUIRED COMPONENTS v2 cereal)
target_link_libraries(my_app PRIVATE kamping::v2 kamping::v2::cereal)
Component |
Target |
Contents |
Option |
|---|---|---|---|
|
|
KaMPIng-v2 (loads |
|
|
|
Distributed algorithms (loads |
|
|
|
Cereal serialization |
|
|
|
Kokkos views |
|
|
|
Thrust device vectors (CUDA) |
|
|
|
SYCL buffers (Intel oneAPI DPC++) |
|
Adapters are installed if they were enabled when installing. Without COMPONENTS, all installed components are
loaded.
A first program¶
Everything is available through a single header; individual headers such as
<kamping/v2/collectives/alltoallv.hpp> keep compile times down. The examples in this documentation use two
namespace aliases:
#include <kamping/v2.hpp>
namespace v2 = kamping::v2;
namespace views = kamping::v2::views;
kamping::v2::environment initializes MPI and finalizes it when it goes out of scope.
kamping::v2::comm_view is a non-owning view of a communicator; any function that expects a communicator
also accepts a plain MPI_Comm.
v2::environment env(argc, argv);
v2::comm_view const world{MPI_COMM_WORLD};
Sending and receiving¶
A std::vector<int> is a buffer: KaMPIng-v2 knows its data pointer, its size and that its elements are sent as
MPI_INT. On the receiving side, the size of the message is often not known in advance. Piping the vector through
views::resize tells the receive to probe the message first and resize the
vector to fit:
if (world.rank() == 0) {
std::vector<int> data(10);
std::iota(data.begin(), data.end(), 0);
// pointer, count and MPI_INT are taken from the vector
v2::send(data, 1, world);
} else if (world.rank() == 1) {
// the size of the message is not known here, so probe for it and resize before receiving
std::vector<int> data;
v2::recv(data | views::resize, world);
std::cout << "rank 1 received " << data.size() << " elements\n";
}
Without views::resize, recv receives into the vector as it is and never communicates beyond the single
MPI_Recv. This is the general rule in KaMPIng-v2: convenience that costs something, such as a probe or an extra
count exchange, only happens if you ask for it at the call site.
Variable-size collectives¶
In an allgatherv, every rank contributes a different number of elements. With plain MPI, you first exchange the
counts, compute displacements with a prefix sum and allocate the receive buffer. kamping::v2::auto_recv_v()
creates a receive buffer that does all three for you:
std::vector<int> mine(static_cast<std::size_t>(world.rank()) + 1, world.rank());
auto [_, all] = v2::allgatherv(mine, v2::auto_recv_v<int>(), world);
// all == {0, 1, 1, 2, 2, 2, ...}: the counts were exchanged,
// the displacements computed and `all` sized to fit
auto_recv_v<int>() is shorthand for a pipeline of three views, which you can also spell out and combine with your
own storage:
std::vector<int> all;
v2::allgatherv(mine, all | views::auto_counts | views::auto_displs | views::resize_v, world);
See Views for all views.
Next steps¶
Data buffers explains what makes a type a buffer and how to adapt your own types.
Views and Receive buffers that size themselves show how to attach counts, datatypes and resizing.
Non-blocking operations covers non-blocking operations and buffer ownership.