ALPSCore reference
mpi.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1998-2018 ALPS Collaboration. See COPYRIGHT.TXT
3  * All rights reserved. Use is subject to license terms. See LICENSE.TXT
4  * For use in publications, see ACKNOWLEDGE.TXT
5  */
6 
17 #ifndef ALPS_UTILITIES_MPI_HPP_INCLUDED_90206380262d48f0bcbe98fd16edd65d
18 #define ALPS_UTILITIES_MPI_HPP_INCLUDED_90206380262d48f0bcbe98fd16edd65d
19 
20 #include <mpi.h>
21 
22 #include <vector>
23 #include <complex>
24 #include <exception> /* for std::uncaught_exception() */
25 #include <functional> /* for std::plus */
26 #include <algorithm> /* for std::max */
27 
28 #include <memory> /* for proper copy/assign of managed communicators */
29 
30 #include <stdexcept>
31 #include <typeinfo>
32 
33 
34 namespace alps {
35  namespace mpi {
36 
37  namespace detail {
39  template <typename T> class mpi_type {};
40 
41 #define ALPS_MPI_DETAIL_MAKETYPE(_mpitype_, _cxxtype_) \
42  template <> \
43  class mpi_type<_cxxtype_> { \
44  public: \
45  typedef _cxxtype_ value_type; \
46  operator MPI_Datatype() { return _mpitype_; } \
47  }
48 
49  ALPS_MPI_DETAIL_MAKETYPE(MPI_CHAR,char);
50  ALPS_MPI_DETAIL_MAKETYPE(MPI_SHORT,signed short int);
51  ALPS_MPI_DETAIL_MAKETYPE(MPI_INT,signed int);
52  ALPS_MPI_DETAIL_MAKETYPE(MPI_LONG,signed long int);
53  // ALPS_MPI_DETAIL_MAKETYPE(MPI_LONG_LONG_INT,signed long long int);
54  ALPS_MPI_DETAIL_MAKETYPE(MPI_LONG_LONG,signed long long int);
55  ALPS_MPI_DETAIL_MAKETYPE(MPI_SIGNED_CHAR,signed char);
56  ALPS_MPI_DETAIL_MAKETYPE(MPI_UNSIGNED_CHAR,unsigned char);
57  ALPS_MPI_DETAIL_MAKETYPE(MPI_UNSIGNED_SHORT,unsigned short int);
58  ALPS_MPI_DETAIL_MAKETYPE(MPI_UNSIGNED,unsigned int);
59  ALPS_MPI_DETAIL_MAKETYPE(MPI_UNSIGNED_LONG,unsigned long int);
60  ALPS_MPI_DETAIL_MAKETYPE(MPI_UNSIGNED_LONG_LONG,unsigned long long int);
61  ALPS_MPI_DETAIL_MAKETYPE(MPI_FLOAT,float);
62  ALPS_MPI_DETAIL_MAKETYPE(MPI_DOUBLE,double);
63  ALPS_MPI_DETAIL_MAKETYPE(MPI_LONG_DOUBLE,long double);
64  // ALPS_MPI_DETAIL_MAKETYPE(MPI_WCHAR,wchar_t);
65  // ALPS_MPI_DETAIL_MAKETYPE(MPI_C_BOOL,_Bool);
66  // ALPS_MPI_DETAIL_MAKETYPE(MPI_INT8_T,int8_t);
67  // ALPS_MPI_DETAIL_MAKETYPE(MPI_INT16_T,int16_t);
68  // ALPS_MPI_DETAIL_MAKETYPE(MPI_INT32_T,int32_t);
69  // ALPS_MPI_DETAIL_MAKETYPE(MPI_INT64_T,int64_t);
70  // ALPS_MPI_DETAIL_MAKETYPE(MPI_UINT8_T,uint8_t);
71  // ALPS_MPI_DETAIL_MAKETYPE(MPI_UINT16_T,uint16_t);
72  // ALPS_MPI_DETAIL_MAKETYPE(MPI_UINT32_T,uint32_t);
73  // ALPS_MPI_DETAIL_MAKETYPE(MPI_UINT64_T,uint64_t);
74  // ALPS_MPI_DETAIL_MAKETYPE(MPI_C_COMPLEX,float _Complex);
75  // ALPS_MPI_DETAIL_MAKETYPE(MPI_C_FLOAT_COMPLEX,float _Complex);
76  // ALPS_MPI_DETAIL_MAKETYPE(MPI_C_DOUBLE_COMPLEX,double _Complex);
77  // ALPS_MPI_DETAIL_MAKETYPE(MPI_C_LONG_DOUBLE_COMPLEX,long double _Complex);
78 #ifdef ALPS_MPI_HAS_MPI_CXX_BOOL
79  ALPS_MPI_DETAIL_MAKETYPE(MPI_CXX_BOOL,bool);
80 #endif
81 
82 #if defined(ALPS_MPI_HAS_MPI_CXX_DOUBLE_COMPLEX) && defined(ALPS_MPI_HAS_MPI_CXX_FLOAT_COMPLEX)
83  ALPS_MPI_DETAIL_MAKETYPE(MPI_CXX_DOUBLE_COMPLEX,std::complex<double>);
84  ALPS_MPI_DETAIL_MAKETYPE(MPI_CXX_FLOAT_COMPLEX,std::complex<float>);
85 #endif
86 
87 #undef ALPS_MPI_DETAIL_MAKETYPE
88  } // detail::
89 
90 
96  };
97 
99 
111  class communicator {
112  std::shared_ptr<MPI_Comm> comm_ptr_;
113 
114  // Internal functor class to destroy communicator when needed
115  struct comm_deleter {
116  void operator()(MPI_Comm* comm_ptr) {
117  int finalized;
118  MPI_Finalized(&finalized);
119  if (!finalized) MPI_Comm_free(comm_ptr);
120  delete comm_ptr;
121  }
122  };
123 
124  public:
125 
127  communicator() : comm_ptr_(new MPI_Comm(MPI_COMM_WORLD)) {} // FIXME? Shall we deprecate it?
128 
129  // FIXME: introduce error checking!!
130 
132 
136  communicator(const MPI_Comm& comm, comm_create_kind kind) {
137  switch (kind) {
138  default:
139  throw std::logic_error("alps::mpi::communicator(): unsupported `kind` argument.");
140  break;
141  case comm_attach:
142  comm_ptr_.reset(new MPI_Comm(comm));
143  break;
144  case take_ownership:
145  comm_ptr_.reset(new MPI_Comm(comm), comm_deleter());
146  break;
147  case comm_duplicate:
148  MPI_Comm* newcomm_ptr=new MPI_Comm();
149  MPI_Comm_dup(comm, newcomm_ptr);
150  comm_ptr_.reset(newcomm_ptr, comm_deleter());
151  break;
152  }
153  }
154 
156  int rank() const {
157  int myrank;
158  MPI_Comm_rank(*comm_ptr_,&myrank);
159  return myrank;
160  }
161 
163  int size() const {
164  int sz;
165  MPI_Comm_size(*comm_ptr_,&sz);
166  return sz;
167  }
168 
170  void barrier() const {
171  MPI_Barrier(*comm_ptr_);
172  }
173 
175  operator MPI_Comm() const {
176  return *comm_ptr_;
177  }
178  };
179 
180 
182  class environment {
183  bool initialized_;
184  bool abort_on_exception_;
185  public:
186 
188  static void abort(int rc=0)
189  {
190  MPI_Abort(MPI_COMM_WORLD,rc);
191  }
192 
194  static bool initialized()
195  {
196  int ini;
197  MPI_Initialized(&ini);
198  return ini;
199  }
200 
202  static bool finalized()
203  {
204  int fin;
205  MPI_Finalized(&fin);
206  return fin;
207  }
208 
210 
217  environment(int& argc, char**& argv, bool abort_on_exception=true)
218  : initialized_(false), abort_on_exception_(abort_on_exception)
219  {
220  if (!initialized()) {
221  MPI_Init(&argc, &argv);
222  initialized_=true;
223  }
224  }
225 
227 
231  environment(bool abort_on_exception=true)
232  : initialized_(false), abort_on_exception_(abort_on_exception)
233  {
234  if (!initialized()) {
235  MPI_Init(NULL,NULL);
236  initialized_=true;
237  }
238  }
239 
241 
245  {
246  if (!initialized_) return; // we are not in control, don't mess up other's logic.
247  if (finalized()) return; // MPI is finalized --- don't touch it.
248  if (abort_on_exception_ && std::uncaught_exception()) {
249  this->abort(255); // FIXME: make the return code configurable?
250  }
251  MPI_Finalize();
252  }
253 
254  };
255 
257  template <typename T>
258  class maximum {
259  public:
260  maximum() { }
261  T operator()(const T& a, const T& b) const {
262  using std::max;
263  return max(a,b);
264  }
265  };
266 
267 
269  template <typename T>
270  void broadcast(const communicator& comm, T* vals, std::size_t count, int root) {
271  MPI_Bcast(vals, count, detail::mpi_type<T>(), root, comm);
272  }
273 
274 #ifndef ALPS_MPI_HAS_MPI_CXX_BOOL
275  inline void broadcast(const communicator& comm, bool* vals, std::size_t count, int root) {
277  // sizeof() returns size in chars (FIXME? should it be bytes?)
278  MPI_Bcast(vals, count*sizeof(bool), MPI_CHAR, root, comm);
279  }
280 #endif /* ALPS_MPI_HAS_MPI_BOOL */
281 
282 
283 #if !defined(ALPS_MPI_HAS_MPI_CXX_DOUBLE_COMPLEX) || !defined(ALPS_MPI_HAS_MPI_CXX_FLOAT_COMPLEX)
284  template <typename T>
286  inline void broadcast(const communicator& comm, std::complex<T>* vals, std::size_t count, int root) {
287  // sizeof() returns size in chars (FIXME? should it be bytes?)
288  MPI_Bcast(vals, count*sizeof(std::complex<T>), MPI_CHAR, root, comm);
289  }
290 #endif
291 
293  template <typename T>
294  void broadcast(const communicator& comm, T& val, int root) {
295  broadcast(comm, &val, 1, root);
296  }
297 
299  // FIXME: what is exception safety status?
300  // FIXME: inline to have it header-only. A tad too complex to be inlined?
301  inline void broadcast(const communicator& comm, std::string& val, int root) {
302  std::size_t root_sz=val.size();
303  broadcast(comm, root_sz, root);
304  if (comm.rank()==root) {
305  // NOTE: at root rank the value being broadcast does not change, so const cast is safe
306  broadcast(comm, const_cast<char*>(val.data()), root_sz, root);
307  } else {
308  // FIXME: not very efficient --- any better way without heap alloc?
309  // Note, there is no guarantee in C++03 that modifying *(&val[0]+i) is safe!
310  std::unique_ptr<char[]> buf(new char[root_sz]);
311  broadcast(comm, buf.get(), root_sz, root);
312  val.assign(buf.get(), root_sz);
313  }
314  }
315 
316 
318  template <typename T>
319  MPI_Datatype get_mpi_datatype(const T&) {
320  return detail::mpi_type<T>();
321  }
322 
324 
325  template <typename T>
326  void all_gather(const communicator& comm, const T& in_val, std::vector<T>& out_vals) {
327  out_vals.resize(comm.size());
328  MPI_Allgather((void*)&in_val, 1, detail::mpi_type<T>(),
329  &out_vals.front(), 1, detail::mpi_type<T>(),
330  comm);
331  }
332 
334  // FIXME: remove T? or just ensure it's a basic MPI type?
335  template <typename OP, typename T>
336  class is_mpi_op {
337  public:
338  static MPI_Op op() {
339  throw std::logic_error(std::string("is_mpi_op() is not implemented, called for types OP=")
340  +typeid(OP).name() + " and T="+typeid(T).name());
341  }
342  };
343 
345  // FIXME: remove T? restrict T?
346  template <typename T>
347  class is_mpi_op<std::plus<T>, T> {
348  public:
349  static MPI_Op op() {
350  return MPI_SUM;
351  }
352  };
353 
355  // FIXME: remove T? restrict T?
356  template <typename T>
357  class is_mpi_op<maximum<T>, T> {
358  public:
359  static MPI_Op op() {
360  return MPI_MAX;
361  }
362  };
363 
365  template <typename T, typename OP>
366  void all_reduce(const alps::mpi::communicator& comm, const T* val, int n,
367  T* out_val, const OP& /*op*/)
368  {
369  if (n<=0) {
370  throw std::invalid_argument("Non-positive array size in mpi::all_reduce()");
371  }
372  // @todo FIXME: implement in-place operations
373  if (val==out_val) {
374  throw std::invalid_argument("Implicit in-place mpi::all_reduce() is not implemented");
375  }
376  MPI_Allreduce(const_cast<T*>(val), out_val, n, detail::mpi_type<T>(),
377  is_mpi_op<OP,T>::op(), comm);
378  }
379 
381  template <typename T, typename OP>
382  void all_reduce(const alps::mpi::communicator& comm, const T& val,
383  T& out_val, const OP& op)
384  {
385  all_reduce(comm, &val, 1, &out_val, op);
386  }
387 
389  template <typename T, typename OP>
390  T all_reduce(const alps::mpi::communicator& comm, const T& val, const OP& op)
391  {
392  T out_val;
393  all_reduce(comm, val, out_val, op);
394  return out_val;
395  }
396 
397  } // mpi::
398 } // alps::
399 
400 
401 #endif /* ALPS_UTILITIES_MPI_HPP_INCLUDED_90206380262d48f0bcbe98fd16edd65d */
do not duplicate, but destroy when going out of scope
Definition: mpi.hpp:95
int size() const
Returns the number of processes in this communicator.
Definition: mpi.hpp:163
void all_reduce(const alps::mpi::communicator &comm, const T *val, int n, T *out_val, const OP &)
Performs MPI_Allreduce for array of a primitive type, T[n].
Definition: mpi.hpp:366
static bool initialized()
Returns initialized status of MPI.
Definition: mpi.hpp:194
duplicate and destroy when going out of scope
Definition: mpi.hpp:94
STL namespace.
environment(int &argc, char **&argv, bool abort_on_exception=true)
Initializes MPI environment unless it&#39;s already active.
Definition: mpi.hpp:217
communicator(const MPI_Comm &comm, comm_create_kind kind)
Creates a communicator object from an MPI communicator.
Definition: mpi.hpp:136
Encapsulation of an MPI communicator and some communicator-related operations.
Definition: mpi.hpp:111
void all_gather(const communicator &comm, const T &in_val, std::vector< T > &out_vals)
performs MPI_Allgather() for primitive type T
Definition: mpi.hpp:326
MPI environment RAII class.
Definition: mpi.hpp:182
static bool finalized()
Returns finalized status of MPI.
Definition: mpi.hpp:202
static void abort(int rc=0)
Call MPI_Abort()
Definition: mpi.hpp:188
Trait for MPI reduction operations.
Definition: mpi.hpp:336
T operator()(const T &a, const T &b) const
Definition: mpi.hpp:261
Class-holder for reduction operations (and a functor) for type T.
Definition: mpi.hpp:258
communicator()
Creates an MPI_COMM_WORLD communicator object.
Definition: mpi.hpp:127
void barrier() const
Barrier on this communicator.
Definition: mpi.hpp:170
int rank() const
Returns process rank in this communicator.
Definition: mpi.hpp:156
do not destroy when going out of scope
Definition: mpi.hpp:93
void broadcast(const communicator &comm, T *vals, std::size_t count, int root)
Broadcasts array vals of a primitive type T, length count on communicator comm with root root ...
Definition: mpi.hpp:270
MPI_Datatype get_mpi_datatype(const T &)
Returns MPI datatype for the value of type T
Definition: mpi.hpp:319
static MPI_Op op()
Definition: mpi.hpp:338
~environment()
Finalizes MPI unless already finalized or was already initilaized when ctor was called.
Definition: mpi.hpp:244
environment(bool abort_on_exception=true)
Initializes MPI environment unless it&#39;s already active.
Definition: mpi.hpp:231
count_type< T >::type count(T const &arg)
Definition: count.hpp:39
comm_create_kind
Possible ways to make a C++ object from MPI communicator.
Definition: mpi.hpp:92