ALPSCore reference
mpi.cpp
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 
8 #include <alps/hdf5.hpp>
9 
10 #ifdef ALPS_HAVE_MPI
11 
12  namespace alps {
13  namespace alps_mpi {
14  namespace detail {
15 
17 
19  inline int checked_mpi_reduce(const void* sendbuf, void* recvbuf, int count,
20  MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
21  {
22  if (count<=0) {
23  throw std::invalid_argument("MPI_Reduce() is called with invalid count="
24  + std::to_string(count)
25  + ALPS_STACKTRACE);
26  }
27  if (sendbuf==recvbuf) {
28  throw std::invalid_argument("MPI_Reduce() is called with sendbuf==recvbuf"
29  + ALPS_STACKTRACE);
30  }
31  // WORKAROUND:
32  // for some reason, OpenMPI 1.6 declares `sendbuf` as `void*`, hence `const_cast`.
33  const int rc=MPI_Reduce(const_cast<void*>(sendbuf), recvbuf, count, datatype, op, root, comm);
34  return rc;
35  }
36 
52  template<typename T, typename S> std::size_t copy_to_buffer(T const & values, std::vector<S> & buffer, std::size_t offset, std::true_type) {
54  std::vector<std::size_t> extent(get_extent(values));
55  std::size_t size = std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>());
56  assert(buffer.size()>=offset+size && "buffer has sufficient size to accommodate values");
58  std::memcpy(&buffer[offset], const_cast<S *>(get_pointer(values)), sizeof(typename hdf5::scalar_type<T>::type) * size);
59  return offset+size;
60  }
61 
75  template<typename T, typename S> std::size_t copy_to_buffer(T const & values, std::vector<S> & buffer, std::size_t offset, std::false_type) {
77  for(typename T::const_iterator it = values.begin(); it != values.end(); ++it)
78  offset = copy_to_buffer(*it, buffer, offset, typename hdf5::is_continuous<typename T::value_type>::type());
79  return offset;
80  }
81 
103  template<typename T, typename S> std::size_t copy_from_buffer(T const & values, std::vector<S> & buffer, std::size_t offset, std::true_type) {
105  std::vector<std::size_t> extent(get_extent(values));
106  std::size_t size = std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>());
108  std::memcpy(const_cast<S *>(get_pointer(values)), &buffer[offset], sizeof(typename hdf5::scalar_type<T>::type) * size);
109  return offset+size;
110  }
111 
129  template<typename T, typename S> std::size_t copy_from_buffer(T const & values, std::vector<S> & buffer, std::size_t offset, std::false_type) {
130  for(typename T::const_iterator it = values.begin(); it != values.end(); ++it)
131  offset = copy_from_buffer(*it, buffer, offset, typename hdf5::is_continuous<typename T::value_type>::type());
132  return offset;
133  }
134 
135  template<typename T, typename Op, typename C> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, Op /*op*/, int root, std::true_type, C) {
136  // using alps::mpi::reduce;
137  // reduce(comm, in_values, op, root);
139  if (comm.rank()==root) {
140  throw std::logic_error("reduce_impl(): 4-arg overload is called by root rank."+ALPS_STACKTRACE);
141  }
142  checked_mpi_reduce((void*)&in_values, NULL, 1, get_mpi_datatype(T()),
143  alps::mpi::is_mpi_op<Op, T>::op(), root, comm);
144 
145  }
146 
147  template<typename T, typename Op> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, Op /*op*/, int root, std::false_type, std::true_type) {
148  typedef typename alps::hdf5::scalar_type<T>::type scalar_type;
150  std::vector<std::size_t> extent(get_extent(in_values));
152 
153  // using boost::mpi::reduce;
154  // reduce(comm, get_pointer(in_values), std::accumulate(extent.begin(), extent.end(), 0), op, root);
155 
157  checked_mpi_reduce(const_cast<scalar_type*>(get_pointer(in_values)), NULL,
158  std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>()),
159  get_mpi_datatype(scalar_type()), alps::mpi::is_mpi_op<Op, scalar_type>::op(), root, comm);
160  }
161 
162  template<typename T, typename Op, typename C> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, T & out_values, Op /*op*/, int root, std::true_type, C) {
163  using alps::mpi::reduce;
164  // using boost::mpi::reduce;
165  // reduce(comm, (T)in_values, out_values, op, root); // TODO: WTF? - why does boost not define unsigned long long as native datatype
167  // if (comm.rank()!=root) {
168  // // usleep((comm.rank()+1)*1000000); // DEBUG!
169  // std::cerr << "DEBUG:WARNING: rank=" << comm.rank() << " is not root=" << root
170  // << " but called 5-argument reduce_impl()." + ALPS_STACKTRACE << std::endl;
171  // }
172  void* sendbuf=const_cast<T*>(&in_values);
173  if (sendbuf == &out_values) {
174  sendbuf=MPI_IN_PLACE;
175  }
176  checked_mpi_reduce(sendbuf, &out_values, 1, get_mpi_datatype(T()),
177  alps::mpi::is_mpi_op<Op, T>::op(), root, comm);
178  }
179 
180  template<typename T, typename Op> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, T & out_values, Op /*op*/, int root, std::false_type, std::true_type) {
181  typedef typename alps::hdf5::scalar_type<T>::type scalar_type;
183  std::vector<std::size_t> extent(get_extent(in_values));
185  set_extent(out_values, std::vector<std::size_t>(extent.begin(), extent.end()));
187 
188  // using boost::mpi::reduce;
189  // reduce(comm, get_pointer(in_values), std::accumulate(extent.begin(), extent.end(), 0), get_pointer(out_values), op, root);
190 
192  checked_mpi_reduce(const_cast<scalar_type*>(get_pointer(in_values)), get_pointer(out_values),
193  std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>()),
194  get_mpi_datatype(scalar_type()), alps::mpi::is_mpi_op<Op, scalar_type>::op(), root, comm);
195  }
196 
197  template<typename T, typename Op> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, Op /*op*/, int root, std::false_type, std::false_type) {
199  if (is_vectorizable(in_values)) {
201  typedef typename alps::hdf5::scalar_type<T>::type scalar_type;
202  std::vector<std::size_t> extent(get_extent(in_values));
203  std::vector<scalar_type> in_buffer(std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>()));
204  using detail::copy_to_buffer;
205  copy_to_buffer(in_values, in_buffer, 0, typename hdf5::is_content_continuous<T>::type());
206 
207  // using boost::mpi::reduce;
208  // reduce(comm, &in_buffer.front(), in_buffer.size(), op, root);
209 
211  checked_mpi_reduce(&in_buffer.front(), NULL, in_buffer.size(), get_mpi_datatype(scalar_type()),
213 
214  } else
215  throw std::logic_error("No alps::mpi::reduce available for this type " + std::string(typeid(T).name()) + ALPS_STACKTRACE);
216  }
217 
218  template<typename T, typename Op> void reduce_impl(const alps::mpi::communicator & comm, T const & in_values, T & out_values, Op /*op*/, int root, std::false_type, std::false_type) {
220  if (is_vectorizable(in_values)) {
222  typedef typename alps::hdf5::scalar_type<T>::type scalar_type;
223  std::vector<std::size_t> extent(get_extent(in_values));
224  std::vector<scalar_type> in_buffer(std::accumulate(extent.begin(), extent.end(), 1, std::multiplies<std::size_t>()));
225  std::vector<scalar_type> out_buffer(in_buffer);
226  using detail::copy_to_buffer;
227  copy_to_buffer(in_values, in_buffer, 0, typename hdf5::is_content_continuous<T>::type());
228 
229  // using boost::mpi::reduce;
230  // reduce(comm, &in_buffer.front(), in_buffer.size(), &out_buffer.front(), op, root);
231 
233  checked_mpi_reduce(&in_buffer.front(), &out_buffer.front(), in_buffer.size(),
234  get_mpi_datatype(scalar_type()),
236 
238  set_extent(out_values, std::vector<std::size_t>(extent.begin(), extent.end()));
239  using detail::copy_from_buffer;
240  copy_from_buffer(out_values, out_buffer, 0, typename hdf5::is_content_continuous<T>::type());
241  } else
242  throw std::logic_error("No alps::mpi::reduce available for this type " + std::string(typeid(T).name()) + ALPS_STACKTRACE);
243  }
244  } // detail::
245 
246  template<typename T, typename Op> void reduce(const alps::mpi::communicator & comm, T const & in_values, Op op, int root) {
247  using detail::reduce_impl;
248  reduce_impl(comm, in_values, op, root, typename std::is_scalar<T>::type(), typename hdf5::is_content_continuous<T>::type());
249  }
250 
251  template<typename T, typename Op> void reduce(const alps::mpi::communicator & comm, T const & in_values, T & out_values, Op op, int root) {
252  using detail::reduce_impl;
253  reduce_impl(comm, in_values, out_values, op, root, typename std::is_scalar<T>::type(), typename hdf5::is_content_continuous<T>::type());
254  }
255 
257 
258  #define ALPS_INST_MPI_REDUCE(T) \
259  template void reduce(const communicator &, T const &, std::plus<T>, int); \
260  template void reduce(const communicator &, T const &, T &, std::plus<T>, int); \
261  template void reduce(const communicator &, std::vector<T> const &, std::plus<T>, int); \
262  template void reduce(const communicator &, std::vector<T> const &, std::vector<T> &, std::plus<T>, int); \
263  template void reduce(const communicator &, std::vector<std::vector<T>> const&, std::plus<T>, int); \
264  template void reduce(const communicator &, std::vector<std::vector<T>> const&, std::vector<std::vector<T>>&, std::plus<T>, int);
265 
266  ALPS_INST_MPI_REDUCE(boost::uint64_t)
267  ALPS_INST_MPI_REDUCE(float)
268  ALPS_INST_MPI_REDUCE(double)
269  ALPS_INST_MPI_REDUCE(long double)
270 
271  } // alps_mpi::
272  } // alps::
273 
274 #endif
bool is_vectorizable(T const &value)
Definition: archive.hpp:288
void set_extent(T &value, std::vector< std::size_t > const &size)
Definition: archive.hpp:284
std::enable_if<!is_sequence< T >::value, std::size_t >::type size(T const &)
Definition: size.hpp:20
std::vector< std::size_t > get_extent(T const &value)
Definition: archive.hpp:280
Encapsulation of an MPI communicator and some communicator-related operations.
Definition: mpi.hpp:111
scalar_type< T >::type * get_pointer(T &value)
Definition: archive.hpp:272
Trait for MPI reduction operations.
Definition: mpi.hpp:336
#define ALPS_STACKTRACE
Definition: stacktrace.hpp:37
int rank() const
Returns process rank in this communicator.
Definition: mpi.hpp:156
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
count_type< T >::type count(T const &arg)
Definition: count.hpp:39