cuDDL
Loading...
Searching...
No Matches
Classes | Public Member Functions | Static Public Member Functions | List of all members
cuddl::shared_tile_counter Class Reference

A tile counter shared by every rank of a communicator, for replica_search::next_tile. More...

Public Member Functions

__host__ shared_tile_counter (shared_tile_counter &&other) noexcept
 
__host__ shared_tile_counter & operator= (shared_tile_counter &&other) noexcept
 
__host__ ~shared_tile_counter ()
 
uint64_t operator() ()
 The next tile number of the search, counting from 0 across every rank.
 
Result< void > reset ()
 Starts the count again at 0 once every rank has finished the previous search.
 

Static Public Member Functions

static Result< shared_tile_counter > make (MPI_Comm comm)
 

Detailed Description

A tile counter shared by every rank of a communicator, for replica_search::next_tile.

Making, resetting and destroying it are collective.

Rank 0 holds the count in an MPI window, and each claim is one remote fetch-and-add: about 20 microseconds across nodes, against tiles that take milliseconds to search. Open MPI completes a claim from another node only while rank 0 is inside MPI, even with hardware atomics: while rank 0 waited on its GPU, claims from the other node took 70-130 ms. Rank 0 therefore runs a thread that drives MPI progress for as long as the counter lives, so MPI must provide MPI_THREAD_MULTIPLE. The thread polls without sleeping, because Open MPI drives one-sided operations only on some progress calls: with a 20 us sleep between polls, claims took 10-21 ms. It yields, so it occupies a core only while no other thread wants one. A counter serves one search; call reset on every rank before the next. A failed claim aborts the communicator, because a lost tile would silently drop pairs from the results.

Definition at line 325 of file distributed.cuh.

Constructor & Destructor Documentation

◆ shared_tile_counter()

__host__ cuddl::shared_tile_counter::shared_tile_counter ( shared_tile_counter &&  other)
inlinenoexcept

Definition at line 327 of file distributed.cuh.

328 : state_(std::move(other.state_)) {}

◆ ~shared_tile_counter()

__host__ cuddl::shared_tile_counter::~shared_tile_counter ( )
inline

Definition at line 333 of file distributed.cuh.

333{} // NOLINT(modernize-use-equals-default)

Member Function Documentation

◆ make()

static Result< shared_tile_counter > cuddl::shared_tile_counter::make ( MPI_Comm  comm)
inlinestatic

Definition at line 335 of file distributed.cuh.

335 {
336 int provided = MPI_THREAD_SINGLE;
337 CUDDL_TRY(detail::mpi_try(MPI_Query_thread(&provided), "MPI_Query_thread"));
338 if (provided < MPI_THREAD_MULTIPLE) {
339 return Err(
341 "shared_tile_counter needs MPI initialised with MPI_THREAD_MULTIPLE"
342 )
343 );
344 }
345 auto state = std::make_unique<shared_state>();
346 state->comm = comm;
347 CUDDL_TRY(detail::mpi_try(MPI_Comm_rank(comm, &state->rank), "MPI_Comm_rank"));
348 auto const bytes = state->rank == 0 ? static_cast<MPI_Aint>(sizeof(uint64_t)) : 0;
349 CUDDL_TRY(
351 MPI_Win_allocate(
352 bytes, sizeof(uint64_t), MPI_INFO_NULL, comm, &state->count, &state->window
353 ),
354 "MPI_Win_allocate"
355 )
356 );
357 CUDDL_TRY(detail::mpi_try(MPI_Win_lock_all(0, state->window), "MPI_Win_lock_all"));
358 state->locked = true;
359 if (state->rank == 0) {
360 *state->count = 0;
361 CUDDL_TRY(detail::mpi_try(MPI_Win_sync(state->window), "MPI_Win_sync"));
362 CUDDL_TRY(
363 detail::mpi_try(MPI_Comm_dup(MPI_COMM_SELF, &state->progress_comm), "MPI_Comm_dup")
364 );
365 // Probing a communicator nobody sends on runs MPI's progress engine, which
366 // completes the claims other ranks have in flight.
367 if (!detail::ablated("progress-thread")) {
368 state->progress =
369 std::jthread([comm = state->progress_comm](const std::stop_token& stop) {
370 while (!stop.stop_requested()) {
371 int flag = 0;
372 MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, comm, &flag, MPI_STATUS_IGNORE);
373 std::this_thread::yield();
374 }
375 });
376 }
377 }
378 CUDDL_TRY(detail::mpi_try(MPI_Barrier(comm), "MPI_Barrier"));
379 shared_tile_counter counter;
380 counter.state_ = std::move(state);
381 return Result<shared_tile_counter>::ok(std::move(counter));
382 }
__host__ shared_tile_counter(shared_tile_counter &&other) noexcept
#define CUDDL_TRY(expr)
Propagates a cuddl::Result failure from the enclosing function (GNU statement expression).
Definition error.hpp:225
constexpr bool ablated(std::string_view) noexcept
Always false: this build compiles the ablation switches out.
Definition ablation.hpp:82
Result< void > mpi_try(int status, char const *call)
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
Definition error.hpp:176
static Error invalid_argument(std::string message)
Definition error.hpp:99
static Result ok(T value)
Definition error.hpp:121
Here is the call graph for this function:

◆ operator()()

uint64_t cuddl::shared_tile_counter::operator() ( )
inline

The next tile number of the search, counting from 0 across every rank.

Definition at line 385 of file distributed.cuh.

385 {
386 std::lock_guard const lock(state_->update);
387 uint64_t const one = 1;
388 uint64_t previous = 0;
389 if (MPI_Fetch_and_op(&one, &previous, MPI_UINT64_T, 0, 0, MPI_SUM, state_->window) !=
390 MPI_SUCCESS ||
391 MPI_Win_flush(0, state_->window) != MPI_SUCCESS) {
392 MPI_Abort(state_->comm, 1);
393 }
394 return previous;
395 }

◆ operator=()

__host__ shared_tile_counter & cuddl::shared_tile_counter::operator= ( shared_tile_counter &&  other)
inlinenoexcept

Definition at line 329 of file distributed.cuh.

329 {
330 state_ = std::move(other.state_);
331 return *this;
332 }

◆ reset()

Result< void > cuddl::shared_tile_counter::reset ( )
inline

Starts the count again at 0 once every rank has finished the previous search.

Definition at line 398 of file distributed.cuh.

398 {
399 CUDDL_TRY(detail::mpi_try(MPI_Barrier(state_->comm), "MPI_Barrier"));
400 if (state_->rank == 0) {
401 uint64_t const zero = 0;
402 CUDDL_TRY(
404 MPI_Accumulate(
405 &zero, 1, MPI_UINT64_T, 0, 0, 1, MPI_UINT64_T, MPI_REPLACE, state_->window
406 ),
407 "MPI_Accumulate"
408 )
409 );
410 CUDDL_TRY(detail::mpi_try(MPI_Win_flush(0, state_->window), "MPI_Win_flush"));
411 }
412 return detail::mpi_try(MPI_Barrier(state_->comm), "MPI_Barrier");
413 }
Here is the call graph for this function:

The documentation for this class was generated from the following file: