Discontinuous Galerkin Library
#include "dg/algorithm.h"
Loading...
Searching...
No Matches
mpi_gather.h
Go to the documentation of this file.
1#pragma once
2#include <cassert>
3#include <thrust/host_vector.h>
4#include <thrust/copy.h>
5
6#include "exceptions.h"
7#include "config.h"
8#include "mpi_datatype.h"
9#include "mpi_permutation.h"
10#include "tensor_traits.h"
11#include "memory.h"
12#include "index.h"
13#ifdef DG_WITH_NCCL
14#include "nccl.h"
16namespace dg
17{
18namespace detail
19{
20
21// Note from ncclCommGetAsyncError documentation: Query progress and/or errors
22// from asynchronous nccl function
23//
24// nccl functions without stream argument are complete as soon as the return
25// value is ncclSuccess. nccl functions with stream arguments return
26// ncclSuccess as soon as the operation kernel is submitted to the stream. If
27// the state is ncclInProgress no other kernels are allowed to be issued on the
28// stream used by nccl (MW: unless it is another nccl function within ncclGroup
29// calls I guess?) If there is an error on a communicator it should be
30// destroyed using ncclCommAbort
31void ncclCommSynchronize(ncclComm_t comm)
32{
33 // Wait untill all Kernels are submitted, or function returns
34 ncclResult_t state;
35 do
36 {
37 ncclResult_t result = ncclCommGetAsyncError( comm, &state);
38 if( result != ncclSuccess)
39 throw dg::Error(dg::Message(_ping_)<<ncclGetErrorString(result));
40 }
41 while( state == ncclInProgress);
42 if( state != ncclSuccess)
43 throw dg::Error(dg::Message(_ping_)<<ncclGetErrorString(state));
44}
45
46// Similar to exblas/mpi_accumulate, this keeps a static map of comms we already created
47inline void getNcclComm( MPI_Comm comm, ncclComm_t * ncclcomm, cudaStream_t * stream )
48{
49 static std::map<MPI_Comm, std::pair<ncclComm_t, cudaStream_t>> comms;
50 if( comms.count(comm) == 1 )
51 {
52 *ncclcomm = std::get<0>(comms[comm]);
53 *stream = std::get<1>(comms[comm]);
54 return;
55 }
56 int rank, size;
57 MPI_Comm_rank( comm, &rank);
58 MPI_Comm_size( comm, &size);
59
60 ncclUniqueId ncclid;
61 // Check the nccl user-guide for options
62 ncclConfig_t config = NCCL_CONFIG_INITIALIZER;
63 config.blocking = 0; // important! We want asynchronous communication (MW but has seg-fault on nccl-2.22 and super slow on nccl-2.28)
64 // If blocking = 1 the ncclCommSynchronize calls should be deleted
65
66 if( rank == 0)
67 ncclGetUniqueId(&ncclid);
68 MPI_Bcast((void *)&ncclid, sizeof(ncclid), MPI_BYTE, 0, comm);
69 ncclCommInitRankConfig( ncclcomm, size, ncclid, rank, &config);
70 ncclCommSynchronize( *ncclcomm);
71
72
73 cudaStreamCreate( stream);
74
75 comms[comm] = {*ncclcomm, *stream};
76}
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;}
88
89// We need to separately catch the complex nature of the array though
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;}
94
95}// namespace detail
96}// namespace dg
98#endif // DG_WITH_NCCL
99
100namespace dg{
101
259namespace detail{
260inline void cuda_check_error_and_sync_device()
261{
262#ifdef __CUDACC__ // g++ does not know cuda code (happens for device=cpu, code must be valid)
263 // cuda - sync device
264 cudaError_t code = cudaGetLastError( );
265 if( code != cudaSuccess)
266 throw dg::Error(dg::Message(_ping_)<<cudaGetErrorString(code));
267 // We have to wait that all kernels are finished and values are
268 // ready to be sent
269 code = cudaDeviceSynchronize();
270 if( code != cudaSuccess)
271 throw dg::Error(dg::Message(_ping_)<<cudaGetErrorString(code));
272#endif // __CUDACC__
273}
274
275// Used for Average operation
276struct MPIAllreduce
277{
278 MPIAllreduce( MPI_Comm comm = MPI_COMM_NULL) : m_comm(comm){}
279 MPI_Comm communicator() const{ return m_comm;}
280 template<class ContainerType> // a Shared Vector
281 void reduce( ContainerType& y) const
282 {
283
285 {
286 detail::cuda_check_error_and_sync_device();
287 if constexpr ( dg::nccl_mpi)
288 nccl_reduce( y);
289 else if constexpr ( dg::cuda_aware_mpi)
290 mpi_reduce( y);
291 else
292 cuda_unaware_reduce( y);
293 }
294 else
295 mpi_reduce(y);
296 }
297 private:
298 template<class ContainerType>
299 void mpi_reduce( ContainerType& y) const
300 {
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);
305 }
306 template<class ContainerType>
307 void nccl_reduce( ContainerType& y) const
308 {
309#ifdef DG_WITH_NCCL
311 ncclComm_t ncclcomm;
312 cudaStream_t stream;
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>)
316 ysize *= 2;
317 // Since this operation is put on stream != default_stream this will execute asynchronously
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);
321 // Wait for Kernel to be issued to stream
322 ncclCommSynchronize( ncclcomm);
323 // Sync the stream
324 cudaStreamSynchronize(stream);
325#endif // DG_WITH_NCCL
326 }
327 template<class ContainerType>
328 void cuda_unaware_reduce( ContainerType& y) const
329 {
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() ); // works even if y is a view
334 mpi_reduce( h_buffer);
335 thrust::copy( h_buffer.begin(), h_buffer.end(), y.begin() ); // works even if y is a view
336 }
337 MPI_Comm m_comm;
338 mutable detail::AnyVector<thrust::host_vector> m_h_buffer;
339};
340
374struct MPIContiguousGather
375{
376 MPIContiguousGather( MPI_Comm comm = MPI_COMM_NULL)
377 : m_comm(comm), m_communicating(false){ }
384 MPIContiguousGather(
385 const std::map<int, thrust::host_vector<MsgChunk>>& recvMsg,
386 MPI_Comm comm)
387 : m_comm(comm), m_recvMsg( recvMsg)
388 {
389 m_sendMsg = mpi_permute ( recvMsg, comm);
390 m_communicating = is_communicating( recvMsg, comm);
391 resize_rqst();
392 m_gatherFrom_minSize = 0;
393 for( auto& chunks : m_sendMsg) // first is PID, second is vector of chunks
394 for( auto& chunk : chunks.second)
395 m_gatherFrom_minSize = std::max( m_gatherFrom_minSize,
396 (unsigned)(chunk.idx+chunk.size));
397 }
398
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)
402 {
403 std::map<int, thrust::host_vector<MsgChunk>> recvChunk;
404 for( auto& idx: recvIdx)
405 {
406 auto chunks = detail::find_contiguous_chunks( idx.second);
407 for( auto& chunk : chunks)
408 {
409 recvChunk[idx.first].push_back( {chunk.idx*chunk_size,
410 chunk.size*chunk_size});
411 }
412 }
413 return recvChunk;
414 }
415
416 MPI_Comm communicator() const{return m_comm;}
418 unsigned buffer_size( bool self_communication = true) const
419 {
420 return msg_size( m_recvMsg, self_communication);
421 }
422
423 bool isCommunicating() const{
424 return m_communicating;
425 }
426 // if not self_communication then buffer can be smaller
427 template<class ContainerType0, class ContainerType1>
428 void global_gather_init( const ContainerType0& gatherFrom, ContainerType1& buffer,
429 bool self_communication = true) const
430 {
431 if( gatherFrom.size() < m_gatherFrom_minSize)
432 throw dg::Error(dg::Message(_ping_)<<"In MPIGather GatherFrom size "
433 <<gatherFrom.size() << " must be at least "<<m_gatherFrom_minSize);
434 if( buffer.size() < buffer_size( self_communication))
435 throw dg::Error(dg::Message(_ping_)<<"In MPIGather buffer size "
436 <<buffer.size() << " must be at least "<<buffer_size( self_communication));
437 int rank;
438 MPI_Comm_rank( m_comm, &rank);
439 // TODO only works if m_recvMsg is non-overlapping
441 static_assert( std::is_same_v<value_type,
442 get_value_type<ContainerType1>>);
444 {
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);
452 else
453 cuda_unaware_global_gather_init( gatherFrom, buffer,
454 self_communication, rank, m_recvMsg, m_sendMsg);
455 }
456 else
457 mpi_global_gather_init( gatherFrom, buffer,
458 self_communication, rank, m_recvMsg, m_sendMsg);
459 }
460
462 template<class ContainerType>
463 void global_gather_wait( ContainerType& buffer) const
464 {
467 {
468 if constexpr ( dg::nccl_mpi)
469 {
470#ifdef DG_WITH_NCCL
471 // Make sure all Send/Recv Kernels are actually issued to the stream
472 ncclComm_t ncclcomm;
473 cudaStream_t stream;
474 detail::getNcclComm( m_comm, &ncclcomm, &stream);
475 ncclCommSynchronize( ncclcomm);
476 cudaStreamSynchronize(stream);
477#endif // DG_WITH_NCCL
478 }
479 else if constexpr ( dg::cuda_aware_mpi)
480 MPI_Waitall( m_rqst.size(), &m_rqst[0], MPI_STATUSES_IGNORE );
481 else
482 {
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() );
486 }
487 }
488 else
489 MPI_Waitall( m_rqst.size(), &m_rqst[0], MPI_STATUSES_IGNORE );
490
491 }
492 private:
493 MPI_Comm m_comm; // from constructor
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; // from constructor
497
498 mutable detail::AnyVector<thrust::host_vector> m_h_buffer;
499
500 unsigned m_gatherFrom_minSize = 0;
501 mutable detail::AnyVector<thrust::host_vector> m_h_store;
502
503 mutable std::vector<MPI_Request> m_rqst;
504 void resize_rqst()
505 {
506 unsigned rqst_size = 0;
507 // number of messages to send and receive
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);
513 }
515 unsigned msg_size( const std::map<int,thrust::host_vector<MsgChunk>>& msg,
516 bool self_communication = true) const
517 {
518 unsigned msg_size = 0;
519 int rank;
520 MPI_Comm_rank( m_comm, &rank);
521 // We need to find out the minimum amount of memory we need to allocate
522 for( auto& chunks : msg) // first is PID, second is vector of chunks
523 for( auto& chunk : chunks.second)
524 {
525 if( chunks.first == rank and not self_communication)
526 continue;
527 msg_size += chunk.size;
528 }
529 return msg_size;
530 }
531 // Size of the h_store
532 unsigned store_size( bool self_communication = true) const
533 {
534 return msg_size( m_sendMsg, self_communication);
535 }
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
541 ) const
542 {
543 // TODO only works if recvMsg is non-overlapping
544 // MW: not sure what exactly can go wrong ...
545 //
547 // Receives (we implicitly receive chunks in the order)
548 unsigned start = 0;
549 unsigned rqst_counter = 0;
550 for( auto& msg : recvMsg) // first is PID, second is vector of chunks
551 for( unsigned u=0; u<msg.second.size(); u++)
552 {
553 if( msg.first == rank and not self_communication)
554 continue;
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>(), //receiver
559 msg.first, u, m_comm, &m_rqst[rqst_counter]); //source
560 rqst_counter ++;
561 start += chunk.size;
562 }
563 // Send
564 for( auto& msg : sendMsg) // first is PID, second is vector of chunks
565 for( unsigned u=0; u<msg.second.size(); u++)
566 {
567 if( msg.first == rank and not self_communication)
568 continue;
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>(), //sender
573 msg.first, u, m_comm, &m_rqst[rqst_counter]); //destination
574 rqst_counter ++;
575 }
576 }
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
582 ) const
583 {
584#ifdef DG_WITH_NCCL
586 ncclComm_t ncclcomm;
587 cudaStream_t stream;
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>)
591 mod_size = 2;
592 ncclGroupStart();
593
594 unsigned start = 0;
595 for( auto& msg : recvMsg) // first is PID, second is vector of chunks
596 for( unsigned u=0; u<msg.second.size(); u++)
597 {
598 if( msg.first == rank and not self_communication)
599 continue;
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);
604 start += chunk.size;
605 }
606 for( auto& msg : sendMsg) // first is PID, second is vector of chunks
607 for( unsigned u=0; u<msg.second.size(); u++)
608 {
609 if( msg.first == rank and not self_communication)
610 continue;
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);
615 }
616 // GroupEnd does not yet mean that all Kernels are submitted, just that grouping is done
617 ncclGroupEnd();
618#endif // DG_WITH_NCCL
619 }
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
625 ) const
626 {
627#ifdef __CUDACC__ // g++ does not know cuda code
628 // if cuda unaware we need to send messages through host
630 m_h_store.template set<value_type>( store_size( self_communication));
631 // BugFix: buffer value_type must be set even if no messages are sent
632 // so that global_gather_wait works
633 m_h_buffer.template set<value_type>( buffer.size()); // global_gather_wait assigns same size
634 auto& h_buffer = m_h_buffer.template get<value_type>();
635 auto& h_store = m_h_store.template get<value_type>();
636 //
637 std::map<int,thrust::host_vector<MsgChunk>> h_sendMsg = sendMsg;
638 unsigned start = 0;
639 for( auto& msg : sendMsg) // first is PID, second is vector of chunks
640 for( unsigned u=0; u<msg.second.size(); u++)
641 {
642 if( msg.first == rank and not self_communication)
643 continue;
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);
648
649 h_sendMsg[msg.first][u].idx = start;
650 start += chunk.size;
651 }
652 mpi_global_gather_init( h_store, h_buffer, self_communication, rank,
653 recvMsg, h_sendMsg);
654#endif // __CUDACC__
655
656 }
657
658};
659}//namespace detail
661
671template< template <class> class Vector>
673{
674
681 MPIGather( MPI_Comm comm = MPI_COMM_NULL) : m_mpi_gather(comm){ }
682
685 const std::map<int, thrust::host_vector<int>>& recvIdx, // should be unique global indices (->gIdx2unique)
686 MPI_Comm comm) : MPIGather( recvIdx, 1, comm)
687 { }
702 const std::map<int, thrust::host_vector<int>>& recvIdx, // in units of chunk size
703 unsigned chunk_size, // can be 1 (contiguous indices in recvIdx are concatenated)
704 MPI_Comm comm)
705 {
706 // TODO Catch wrong size of recvIdx
708 "Only Shared vectors allowed");
709 // The idea is that recvIdx and sendIdx completely define the communication pattern
710 // and we can choose an optimal implementation
711 // Actually the MPI library should do this but general gather indices
712 // don't seem to be supported
713 // So for now let's just use MPI_Alltoallv
714 // Possible optimization includes
715 // - check for duplicate messages to save internal buffer memory
716 // - check for contiguous messages to avoid internal buffer memory
717 // - check if an MPI_Type could fit the index map
718 //v -> store -> buffer -> w
719 // G_1 P G_2 v
720 // G_2^T P^T G_1^T w
721 // We need some logic to determine if we should pre-gather messages into a store
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(); // size of vector of chunks
727 unsigned size = recvChunks.size(); // number of pids involved
728 double avg_msg_per_pid = (double)num_messages/(double)size; // > 1
729 MPI_Allreduce( MPI_IN_PLACE, &avg_msg_per_pid, 1, MPI_DOUBLE, MPI_MAX, comm);
730 m_contiguous = ( avg_msg_per_pid < 10); // 10 is somewhat arbitrary
731 if( not m_contiguous) // messages are too fractioned
732 {
733 m_contiguous = false;
734 // bootstrap communication pattern
735 auto sendIdx = mpi_permute ( recvIdx, comm);
736 auto lIdx = detail::flatten_map(sendIdx);
737 thrust::host_vector<int> g2;
738 unsigned start = 0;
739 for( auto& idx : sendIdx)
740 {
741 for( unsigned l=0; l<idx.second.size(); l++)
742 {
743 for( unsigned k=0; k<chunk_size; k++)
744 {
745 g2.push_back(idx.second[l] + k);
746 }
747 idx.second[l] = start + l; // repoint index to store index in units of chunk_size
748 }
749 start += idx.second.size();
750 }
751 m_g2 = g2;
752 // bootstrap communication pattern back to recvIdx
753 // (so that recvIdx has index into same store)
754 auto store_recvIdx = mpi_permute( sendIdx, comm);
755 auto store_recvChunks = detail::MPIContiguousGather::make_chunks(
756 store_recvIdx, chunk_size);
757 m_mpi_gather = detail::MPIContiguousGather( store_recvChunks, comm);
758 }
759 else
760 m_mpi_gather = detail::MPIContiguousGather( recvChunks, comm);
761
762 }
783 template<class ArrayVec = thrust::host_vector<std::array<int,2>>,
784 class IntVec = thrust::host_vector<int>>
786 const ArrayVec& gather_map,
787 IntVec& bufferIdx,
788 MPI_Comm comm)
789 : MPIGather( gIdx2unique_idx ( gather_map, bufferIdx), comm)
790 {
791 }
792
794 template< template<class> class OtherVector>
795 friend struct MPIGather; // enable copy
796
805 template< template<typename > typename OtherVector>
807 : m_contiguous( src.m_contiguous),
808 m_g2( src.m_g2),
809 m_mpi_gather( src.m_mpi_gather)
810 // we don't need to copy memory buffers (they are just workspace) or the request
811 {
812 }
813
814
820 MPI_Comm communicator() const{return m_mpi_gather.communicator();}
821
823 bool isContiguous() const { return m_contiguous;}
836 unsigned buffer_size() const { return m_mpi_gather.buffer_size();}
837
859 bool isCommunicating() const
860 {
861 return m_mpi_gather.isCommunicating();
862 }
880 template<class ContainerType0, class ContainerType1>
881 void global_gather_init( const ContainerType0& gatherFrom, ContainerType1& buffer) const
882 {
884 if( not m_contiguous)
885 {
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(),
889 store.begin());
890 m_mpi_gather.global_gather_init( store, buffer);
891 }
892 else
893 m_mpi_gather.global_gather_init( gatherFrom, buffer);
894 }
906 template<class ContainerType>
907 void global_gather_wait( ContainerType& buffer) const
908 {
909 m_mpi_gather.global_gather_wait( buffer);
910 }
911
912 private:
913 bool m_contiguous = false;
914 Vector<int> m_g2;
915 dg::detail::MPIContiguousGather m_mpi_gather;
916 // These are mutable and we never expose them to the user
917 //unsigned m_store_size;// not needed because g2.index_map.size()
918 mutable detail::AnyVector< Vector> m_store;
919
920};
921
922}//namespace dg
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
@ y
y direction
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
double value_type