3#include <thrust/host_vector.h>
4#include <thrust/copy.h>
31void ncclCommSynchronize(ncclComm_t comm)
37 ncclResult_t result = ncclCommGetAsyncError( comm, &state);
38 if( result != ncclSuccess)
41 while( state == ncclInProgress);
42 if( state != ncclSuccess)
47inline void getNcclComm( MPI_Comm comm, ncclComm_t * ncclcomm, cudaStream_t * stream )
49 static std::map<MPI_Comm, std::pair<ncclComm_t, cudaStream_t>> comms;
50 if( comms.count(comm) == 1 )
52 *ncclcomm = std::get<0>(comms[comm]);
53 *stream = std::get<1>(comms[comm]);
57 MPI_Comm_rank( comm, &rank);
58 MPI_Comm_size( comm, &size);
62 ncclConfig_t config = NCCL_CONFIG_INITIALIZER;
67 ncclGetUniqueId(&ncclid);
68 MPI_Bcast((
void *)&ncclid,
sizeof(ncclid), MPI_BYTE, 0, comm);
69 ncclCommInitRankConfig( ncclcomm, size, ncclid, rank, &config);
70 ncclCommSynchronize( *ncclcomm);
73 cudaStreamCreate( stream);
75 comms[comm] = {*ncclcomm, *stream};
77template<
class value_type>
78inline ncclDataType_t getNcclDataType(){ assert(
false &&
"Type not supported!\n" );
return ncclDataType_t{}; }
79template<>
inline ncclDataType_t getNcclDataType<char>(){
return ncclChar;}
80template<>
inline ncclDataType_t getNcclDataType<signed char>(){
return ncclChar;}
81template<>
inline ncclDataType_t getNcclDataType<unsigned char>(){
return ncclUint8;}
82template<>
inline ncclDataType_t getNcclDataType<int>(){
return ncclInt;}
83template<>
inline ncclDataType_t getNcclDataType<unsigned int>(){
return ncclUint32;}
84template<>
inline ncclDataType_t getNcclDataType<signed long int>(){
return ncclInt64;}
85template<>
inline ncclDataType_t getNcclDataType<unsigned long int>(){
return ncclUint64;}
86template<>
inline ncclDataType_t getNcclDataType<float>(){
return ncclFloat;}
87template<>
inline ncclDataType_t getNcclDataType<double>(){
return ncclDouble;}
90template<>
inline ncclDataType_t getNcclDataType<std::complex<float>>(){
return ncclFloat;}
91template<>
inline ncclDataType_t getNcclDataType<std::complex<double>>(){
return ncclDouble;}
92template<>
inline ncclDataType_t getNcclDataType<thrust::complex<float>>(){
return ncclFloat;}
93template<>
inline ncclDataType_t getNcclDataType<thrust::complex<double>>(){
return ncclDouble;}
260inline void cuda_check_error_and_sync_device()
264 cudaError_t code = cudaGetLastError( );
265 if( code != cudaSuccess)
269 code = cudaDeviceSynchronize();
270 if( code != cudaSuccess)
278 MPIAllreduce( MPI_Comm comm = MPI_COMM_NULL) : m_comm(comm){}
279 MPI_Comm communicator()
const{
return m_comm;}
280 template<
class ContainerType>
281 void reduce( ContainerType& y)
const
286 detail::cuda_check_error_and_sync_device();
287 if constexpr ( dg::nccl_mpi)
289 else if constexpr ( dg::cuda_aware_mpi)
292 cuda_unaware_reduce( y);
298 template<
class ContainerType>
299 void mpi_reduce( ContainerType& y)
const
302 void * send_ptr = thrust::raw_pointer_cast(
y.data());
303 MPI_Allreduce( MPI_IN_PLACE, send_ptr,
y.size(),
304 getMPIDataType<value_type>(), MPI_SUM, m_comm);
306 template<
class ContainerType>
307 void nccl_reduce( ContainerType& y)
const
313 detail::getNcclComm( m_comm, &ncclcomm, &stream);
314 unsigned ysize =
y.size();
315 if constexpr( std::is_same_v<dg::get_tensor_category<value_type>,
dg::ComplexTag>)
318 void * send_ptr = thrust::raw_pointer_cast(
y.data());
319 ncclAllReduce( send_ptr, send_ptr, ysize,
320 detail::getNcclDataType<value_type>(), ncclSum, ncclcomm, stream);
322 ncclCommSynchronize( ncclcomm);
324 cudaStreamSynchronize(stream);
327 template<
class ContainerType>
328 void cuda_unaware_reduce( ContainerType& y)
const
331 m_h_buffer.template set<value_type>(
y.size());
332 auto& h_buffer = m_h_buffer.template get<value_type>();
333 thrust::copy(
y.begin(),
y.end(), h_buffer.begin() );
334 mpi_reduce( h_buffer);
335 thrust::copy( h_buffer.begin(), h_buffer.end(),
y.begin() );
338 mutable detail::AnyVector<thrust::host_vector> m_h_buffer;
374struct MPIContiguousGather
376 MPIContiguousGather( MPI_Comm comm = MPI_COMM_NULL)
377 : m_comm(comm), m_communicating(false){ }
385 const std::map<
int, thrust::host_vector<MsgChunk>>& recvMsg,
387 : m_comm(comm), m_recvMsg( recvMsg)
390 m_communicating = is_communicating( recvMsg, comm);
392 m_gatherFrom_minSize = 0;
393 for(
auto& chunks : m_sendMsg)
394 for(
auto& chunk : chunks.second)
395 m_gatherFrom_minSize = std::max( m_gatherFrom_minSize,
396 (
unsigned)(chunk.idx+chunk.size));
400 static const std::map<int, thrust::host_vector<MsgChunk>> make_chunks(
401 const std::map<
int, thrust::host_vector<int> > &recvIdx,
int chunk_size = 1)
403 std::map<int, thrust::host_vector<MsgChunk>> recvChunk;
404 for(
auto& idx: recvIdx)
406 auto chunks = detail::find_contiguous_chunks( idx.second);
407 for(
auto& chunk : chunks)
409 recvChunk[idx.first].push_back( {chunk.idx*chunk_size,
410 chunk.size*chunk_size});
416 MPI_Comm communicator()
const{
return m_comm;}
418 unsigned buffer_size(
bool self_communication =
true)
const
420 return msg_size( m_recvMsg, self_communication);
423 bool isCommunicating()
const{
424 return m_communicating;
427 template<
class ContainerType0,
class ContainerType1>
428 void global_gather_init(
const ContainerType0& gatherFrom, ContainerType1& buffer,
429 bool self_communication =
true)
const
431 if( gatherFrom.size() < m_gatherFrom_minSize)
433 <<gatherFrom.size() <<
" must be at least "<<m_gatherFrom_minSize);
434 if( buffer.size() < buffer_size( self_communication))
436 <<buffer.size() <<
" must be at least "<<buffer_size( self_communication));
438 MPI_Comm_rank( m_comm, &rank);
442 get_value_type<ContainerType1>>);
445 detail::cuda_check_error_and_sync_device();
446 if constexpr ( dg::nccl_mpi)
447 nccl_global_gather_init( gatherFrom, buffer,
448 self_communication, rank, m_recvMsg, m_sendMsg);
449 else if constexpr ( dg::cuda_aware_mpi)
450 mpi_global_gather_init( gatherFrom, buffer,
451 self_communication, rank, m_recvMsg, m_sendMsg);
453 cuda_unaware_global_gather_init( gatherFrom, buffer,
454 self_communication, rank, m_recvMsg, m_sendMsg);
457 mpi_global_gather_init( gatherFrom, buffer,
458 self_communication, rank, m_recvMsg, m_sendMsg);
462 template<
class ContainerType>
463 void global_gather_wait( ContainerType& buffer)
const
468 if constexpr ( dg::nccl_mpi)
474 detail::getNcclComm( m_comm, &ncclcomm, &stream);
475 ncclCommSynchronize( ncclcomm);
476 cudaStreamSynchronize(stream);
479 else if constexpr ( dg::cuda_aware_mpi)
480 MPI_Waitall( m_rqst.size(), &m_rqst[0], MPI_STATUSES_IGNORE );
483 MPI_Waitall( m_rqst.size(), &m_rqst[0], MPI_STATUSES_IGNORE );
484 auto & h_buffer = m_h_buffer.template get<value_type>();
485 thrust::copy( h_buffer.begin(), h_buffer.end(), buffer.begin() );
489 MPI_Waitall( m_rqst.size(), &m_rqst[0], MPI_STATUSES_IGNORE );
494 bool m_communicating =
false;
495 std::map<int,thrust::host_vector<MsgChunk>> m_sendMsg;
496 std::map<int,thrust::host_vector<MsgChunk>> m_recvMsg;
498 mutable detail::AnyVector<thrust::host_vector> m_h_buffer;
500 unsigned m_gatherFrom_minSize = 0;
501 mutable detail::AnyVector<thrust::host_vector> m_h_store;
503 mutable std::vector<MPI_Request> m_rqst;
506 unsigned rqst_size = 0;
508 for(
auto& msg : m_recvMsg)
509 rqst_size += msg.second.size();
510 for(
auto& msg : m_sendMsg)
511 rqst_size += msg.second.size();
512 m_rqst.resize( rqst_size, MPI_REQUEST_NULL);
515 unsigned msg_size(
const std::map<
int,thrust::host_vector<MsgChunk>>& msg,
516 bool self_communication =
true)
const
518 unsigned msg_size = 0;
520 MPI_Comm_rank( m_comm, &rank);
522 for(
auto& chunks : msg)
523 for(
auto& chunk : chunks.second)
525 if( chunks.first == rank and not self_communication)
527 msg_size += chunk.size;
532 unsigned store_size(
bool self_communication =
true)
const
534 return msg_size( m_sendMsg, self_communication);
536 template<
class ContainerType0,
class ContainerType1>
537 void mpi_global_gather_init(
const ContainerType0& gatherFrom, ContainerType1& buffer,
538 bool self_communication,
int rank,
539 const std::map<
int,thrust::host_vector<MsgChunk>>& recvMsg,
540 const std::map<
int,thrust::host_vector<MsgChunk>>& sendMsg
549 unsigned rqst_counter = 0;
550 for(
auto& msg : recvMsg)
551 for(
unsigned u=0; u<msg.second.size(); u++)
553 if( msg.first == rank and not self_communication)
555 auto chunk = msg.second[u];
556 void * recv_ptr = thrust::raw_pointer_cast( buffer.data()) + start;
557 MPI_Irecv( recv_ptr, chunk.size,
558 getMPIDataType<value_type>(),
559 msg.first, u, m_comm, &m_rqst[rqst_counter]);
564 for(
auto& msg : sendMsg)
565 for(
unsigned u=0; u<msg.second.size(); u++)
567 if( msg.first == rank and not self_communication)
569 auto chunk = msg.second[u];
570 const void * send_ptr = thrust::raw_pointer_cast(gatherFrom.data()) + chunk.idx;
571 MPI_Isend( send_ptr, chunk.size,
572 getMPIDataType<value_type>(),
573 msg.first, u, m_comm, &m_rqst[rqst_counter]);
577 template<
class ContainerType0,
class ContainerType1>
578 void nccl_global_gather_init(
const ContainerType0& gatherFrom, ContainerType1& buffer,
579 bool self_communication,
int rank,
580 const std::map<
int,thrust::host_vector<MsgChunk>>& recvMsg,
581 const std::map<
int,thrust::host_vector<MsgChunk>>& sendMsg
588 detail::getNcclComm( m_comm, &ncclcomm, &stream);
589 unsigned mod_size = 1;
590 if constexpr( std::is_same_v<dg::get_tensor_category<value_type>,
dg::ComplexTag>)
595 for(
auto& msg : recvMsg)
596 for(
unsigned u=0; u<msg.second.size(); u++)
598 if( msg.first == rank and not self_communication)
600 auto chunk = msg.second[u];
601 void * recv_ptr = thrust::raw_pointer_cast( buffer.data()) + start;
602 ncclRecv( recv_ptr, chunk.size*mod_size,
603 detail::getNcclDataType<value_type>(), msg.first, ncclcomm, stream);
606 for(
auto& msg : sendMsg)
607 for(
unsigned u=0; u<msg.second.size(); u++)
609 if( msg.first == rank and not self_communication)
611 auto chunk = msg.second[u];
612 const void * send_ptr = thrust::raw_pointer_cast(gatherFrom.data()) + chunk.idx;
613 ncclSend( send_ptr, chunk.size*mod_size,
614 detail::getNcclDataType<value_type>(), msg.first, ncclcomm, stream);
620 template<
class ContainerType0,
class ContainerType1>
621 void cuda_unaware_global_gather_init(
const ContainerType0& gatherFrom, ContainerType1& buffer,
622 bool self_communication,
int rank,
623 const std::map<
int,thrust::host_vector<MsgChunk>>& recvMsg,
624 const std::map<
int,thrust::host_vector<MsgChunk>>& sendMsg
630 m_h_store.template set<value_type>( store_size( self_communication));
633 m_h_buffer.template set<value_type>( buffer.size());
634 auto& h_buffer = m_h_buffer.template get<value_type>();
635 auto& h_store = m_h_store.template get<value_type>();
637 std::map<int,thrust::host_vector<MsgChunk>> h_sendMsg = sendMsg;
639 for(
auto& msg : sendMsg)
640 for(
unsigned u=0; u<msg.second.size(); u++)
642 if( msg.first == rank and not self_communication)
644 auto chunk = msg.second[u];
645 const void * send_ptr = thrust::raw_pointer_cast(gatherFrom.data()) + chunk.idx;
646 thrust::copy( gatherFrom.begin()+chunk.idx,
647 gatherFrom.begin()+chunk.idx+chunk.size, h_store.begin() + start);
649 h_sendMsg[msg.first][u].idx = start;
652 mpi_global_gather_init( h_store, h_buffer, self_communication, rank,
671template<
template <
class>
class Vector>
681 MPIGather( MPI_Comm comm = MPI_COMM_NULL) : m_mpi_gather(comm){ }
685 const std::map<
int, thrust::host_vector<int>>& recvIdx,
686 MPI_Comm comm) :
MPIGather( recvIdx, 1, comm)
702 const std::map<
int, thrust::host_vector<int>>& recvIdx,
708 "Only Shared vectors allowed");
722 unsigned num_messages = 0;
723 auto recvChunks = detail::MPIContiguousGather::make_chunks(
724 recvIdx, chunk_size);
725 for(
auto& chunks : recvChunks)
726 num_messages+= chunks.second.size();
727 unsigned size = recvChunks.size();
728 double avg_msg_per_pid = (double)num_messages/(
double)size;
729 MPI_Allreduce( MPI_IN_PLACE, &avg_msg_per_pid, 1, MPI_DOUBLE, MPI_MAX, comm);
730 m_contiguous = ( avg_msg_per_pid < 10);
731 if( not m_contiguous)
733 m_contiguous =
false;
736 auto lIdx = detail::flatten_map(sendIdx);
737 thrust::host_vector<int> g2;
739 for(
auto& idx : sendIdx)
741 for(
unsigned l=0; l<idx.second.size(); l++)
743 for(
unsigned k=0; k<chunk_size; k++)
745 g2.push_back(idx.second[l] + k);
747 idx.second[l] = start + l;
749 start += idx.second.size();
755 auto store_recvChunks = detail::MPIContiguousGather::make_chunks(
756 store_recvIdx, chunk_size);
757 m_mpi_gather = detail::MPIContiguousGather( store_recvChunks, comm);
760 m_mpi_gather = detail::MPIContiguousGather( recvChunks, comm);
783 template<
class ArrayVec = thrust::host_vector<std::array<
int,2>>,
784 class IntVec = thrust::host_vector<
int>>
786 const ArrayVec& gather_map,
789 :
MPIGather( gIdx2unique_idx ( gather_map, bufferIdx), comm)
794 template<
template<
class>
class OtherVector>
805 template<
template<
typename >
typename OtherVector>
807 : m_contiguous( src.m_contiguous),
809 m_mpi_gather( src.m_mpi_gather)
836 unsigned buffer_size()
const {
return m_mpi_gather.buffer_size();}
861 return m_mpi_gather.isCommunicating();
880 template<
class ContainerType0,
class ContainerType1>
884 if( not m_contiguous)
886 m_store.template set<value_type>( m_g2.size());
887 auto& store = m_store.template get<value_type>();
888 thrust::gather( m_g2.begin(), m_g2.end(), gatherFrom.begin(),
890 m_mpi_gather.global_gather_init( store, buffer);
893 m_mpi_gather.global_gather_init( gatherFrom, buffer);
906 template<
class ContainerType>
909 m_mpi_gather.global_gather_wait( buffer);
913 bool m_contiguous =
false;
915 dg::detail::MPIContiguousGather m_mpi_gather;
918 mutable detail::AnyVector< Vector> m_store;
class intended for the use in throw statements
Definition exceptions.h:83
small class holding a stringstream
Definition exceptions.h:29
Error classes or the dg library.
#define _ping_
Definition exceptions.h:12
OutputType reduce(const ContainerType &x, OutputType zero, BinaryOp binary_op, UnaryOp unary_op=UnaryOp())
Custom (transform) reduction
Definition blas1.h:215
constexpr bool is_vector_v
Utility typedef.
Definition predicate.h:75
constexpr bool has_policy_v
Utility typedef.
Definition predicate.h:86
typename TensorTraits< std::decay_t< Vector > >::value_type get_value_type
Definition tensor_traits.h:45
std::map< int, MessageType > mpi_permute(const std::map< int, MessageType > &messages, MPI_Comm comm)
Exchange messages between processes in a communicator.
Definition mpi_permutation.h:90
This is the namespace for all functions and classes defined and used by the discontinuous Galerkin li...
complex number type
Definition scalar_categories.h:21
Optimized MPI Gather operation.
Definition mpi_gather.h:673
MPIGather(MPI_Comm comm=MPI_COMM_NULL)
no communication
Definition mpi_gather.h:681
MPIGather(const std::map< int, thrust::host_vector< int > > &recvIdx, MPI_Comm comm)
Short for MPIGather( recvIdx, 1, comm)
Definition mpi_gather.h:684
void global_gather_init(const ContainerType0 &gatherFrom, ContainerType1 &buffer) const
. Globally (across processes) asynchronously gather data into a buffer
Definition mpi_gather.h:881
unsigned buffer_size() const
The local size of the buffer vector w = local map size.
Definition mpi_gather.h:836
MPIGather(const ArrayVec &gather_map, IntVec &bufferIdx, MPI_Comm comm)
Convert an unsorted and possible duplicate global index list to unique stable_sorted by pid and dupli...
Definition mpi_gather.h:785
bool isContiguous() const
Check whether the message from the constructor is contiguous in memory.
Definition mpi_gather.h:823
MPIGather(const std::map< int, thrust::host_vector< int > > &recvIdx, unsigned chunk_size, MPI_Comm comm)
Construct from global index map.
Definition mpi_gather.h:701
void global_gather_wait(ContainerType &buffer) const
Wait for asynchronous communication to finish and gather received data into buffer.
Definition mpi_gather.h:907
MPI_Comm communicator() const
The internal MPI communicator used.
Definition mpi_gather.h:820
bool isCommunicating() const
True if the gather/scatter operation involves actual MPI communication.
Definition mpi_gather.h:859
MPIGather(const MPIGather< OtherVector > &src)
Construct from other execution policy.
Definition mpi_gather.h:806
Indicate a contiguous chunk of shared memory.
Definition vector_categories.h:41