1 // Copyright (C) 2005, 2006 Douglas Gregor.
2 
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 // Message Passing Interface 1.1 -- Section 4.6. Scatter
8 #ifndef BOOST_MPI_SCATTER_HPP
9 #define BOOST_MPI_SCATTER_HPP
10 
11 #include <boost/mpi/exception.hpp>
12 #include <boost/mpi/datatype.hpp>
13 #include <vector>
14 #include <boost/mpi/packed_oarchive.hpp>
15 #include <boost/mpi/packed_iarchive.hpp>
16 #include <boost/mpi/detail/point_to_point.hpp>
17 #include <boost/mpi/communicator.hpp>
18 #include <boost/mpi/environment.hpp>
19 #include <boost/mpi/detail/offsets.hpp>
20 #include <boost/mpi/detail/antiques.hpp>
21 #include <boost/assert.hpp>
22 
23 namespace boost { namespace mpi {
24 
25 namespace detail {
26 // We're scattering from the root for a type that has an associated MPI
27 // datatype, so we'll use MPI_Scatter to do all of the work.
28 template<typename T>
29 void
scatter_impl(const communicator & comm,const T * in_values,T * out_values,int n,int root,mpl::true_)30 scatter_impl(const communicator& comm, const T* in_values, T* out_values,
31              int n, int root, mpl::true_)
32 {
33   MPI_Datatype type = get_mpi_datatype<T>(*in_values);
34   BOOST_MPI_CHECK_RESULT(MPI_Scatter,
35                          (const_cast<T*>(in_values), n, type,
36                           out_values, n, type, root, comm));
37 }
38 
39 // We're scattering from a non-root for a type that has an associated MPI
40 // datatype, so we'll use MPI_Scatter to do all of the work.
41 template<typename T>
42 void
scatter_impl(const communicator & comm,T * out_values,int n,int root,mpl::true_)43 scatter_impl(const communicator& comm, T* out_values, int n, int root,
44              mpl::true_)
45 {
46   MPI_Datatype type = get_mpi_datatype<T>(*out_values);
47   BOOST_MPI_CHECK_RESULT(MPI_Scatter,
48                          (0, n, type,
49                           out_values, n, type,
50                           root, comm));
51 }
52 
53 // Fill the sendbuf while keeping trac of the slot's footprints
54 // Used in the first steps of both scatter and scatterv
55 // Nslots contains the number of slots being sent
56 // to each process (identical values for scatter).
57 // skiped_slots, if present, is deduced from the
58 // displacement array authorised be the MPI API,
59 // for some yet to be determined reason.
60 template<typename T>
61 void
fill_scatter_sendbuf(const communicator & comm,T const * values,int const * nslots,int const * skipped_slots,packed_oarchive::buffer_type & sendbuf,std::vector<int> & archsizes)62 fill_scatter_sendbuf(const communicator& comm, T const* values,
63                      int const* nslots, int const* skipped_slots,
64                      packed_oarchive::buffer_type& sendbuf, std::vector<int>& archsizes) {
65   int nproc = comm.size();
66   archsizes.resize(nproc);
67 
68   for (int dest = 0; dest < nproc; ++dest) {
69     if (skipped_slots) { // wee need to keep this for backward compatibility
70       for(int k= 0; k < skipped_slots[dest]; ++k) ++values;
71     }
72     packed_oarchive procarchive(comm);
73     for (int i = 0; i < nslots[dest]; ++i) {
74       procarchive << *values++;
75     }
76     int archsize = procarchive.size();
77     sendbuf.resize(sendbuf.size() + archsize);
78     archsizes[dest] = archsize;
79     char const* aptr = static_cast<char const*>(procarchive.address());
80     std::copy(aptr, aptr+archsize, sendbuf.end()-archsize);
81   }
82 }
83 
84 template<typename T, class A>
85 T*
86 non_const_data(std::vector<T,A> const& v) {
87   using detail::c_data;
88   return const_cast<T*>(c_data(v));
89 }
90 
91 // Dispatch the sendbuf among proc.
92 // Used in the second steps of both scatter and scatterv
93 // in_value is only provide in the non variadic case.
94 template<typename T>
95 void
dispatch_scatter_sendbuf(const communicator & comm,packed_oarchive::buffer_type const & sendbuf,std::vector<int> const & archsizes,T const * in_values,T * out_values,int n,int root)96 dispatch_scatter_sendbuf(const communicator& comm,
97                          packed_oarchive::buffer_type const& sendbuf, std::vector<int> const& archsizes,
98                          T const* in_values,
99                          T* out_values, int n, int root) {
100   // Distribute the sizes
101   int myarchsize;
102   BOOST_MPI_CHECK_RESULT(MPI_Scatter,
103                          (non_const_data(archsizes), 1, MPI_INT,
104                           &myarchsize, 1, MPI_INT, root, comm));
105   std::vector<int> offsets;
106   if (root == comm.rank()) {
107     sizes2offsets(archsizes, offsets);
108   }
109   // Get my proc archive
110   packed_iarchive::buffer_type recvbuf;
111   recvbuf.resize(myarchsize);
112   BOOST_MPI_CHECK_RESULT(MPI_Scatterv,
113                          (non_const_data(sendbuf), non_const_data(archsizes), c_data(offsets), MPI_BYTE,
114                           c_data(recvbuf), recvbuf.size(), MPI_BYTE,
115                           root, MPI_Comm(comm)));
116   // Unserialize
117   if ( in_values != 0 && root == comm.rank()) {
118     // Our own local values are already here: just copy them.
119     std::copy(in_values + root * n, in_values + (root + 1) * n, out_values);
120   } else {
121     // Otherwise deserialize:
122     packed_iarchive iarchv(comm, recvbuf);
123     for (int i = 0; i < n; ++i) {
124       iarchv >> out_values[i];
125     }
126   }
127 }
128 
129 // We're scattering from the root for a type that does not have an
130 // associated MPI datatype, so we'll need to serialize it.
131 template<typename T>
132 void
scatter_impl(const communicator & comm,const T * in_values,T * out_values,int n,int root,mpl::false_)133 scatter_impl(const communicator& comm, const T* in_values, T* out_values,
134              int n, int root, mpl::false_)
135 {
136   packed_oarchive::buffer_type sendbuf;
137   std::vector<int> archsizes;
138 
139   if (root == comm.rank()) {
140     std::vector<int> nslots(comm.size(), n);
141     fill_scatter_sendbuf(comm, in_values, c_data(nslots), (int const*)0, sendbuf, archsizes);
142   }
143   dispatch_scatter_sendbuf(comm, sendbuf, archsizes, in_values, out_values, n, root);
144 }
145 
146 template<typename T>
147 void
scatter_impl(const communicator & comm,T * out_values,int n,int root,mpl::false_ is_mpi_type)148 scatter_impl(const communicator& comm, T* out_values, int n, int root,
149              mpl::false_ is_mpi_type)
150 {
151   scatter_impl(comm, (T const*)0, out_values, n, root, is_mpi_type);
152 }
153 } // end namespace detail
154 
155 template<typename T>
156 void
scatter(const communicator & comm,const T * in_values,T & out_value,int root)157 scatter(const communicator& comm, const T* in_values, T& out_value, int root)
158 {
159   detail::scatter_impl(comm, in_values, &out_value, 1, root, is_mpi_datatype<T>());
160 }
161 
162 template<typename T>
163 void
scatter(const communicator & comm,const std::vector<T> & in_values,T & out_value,int root)164 scatter(const communicator& comm, const std::vector<T>& in_values, T& out_value,
165         int root)
166 {
167   using detail::c_data;
168   ::boost::mpi::scatter<T>(comm, c_data(in_values), out_value, root);
169 }
170 
171 template<typename T>
scatter(const communicator & comm,T & out_value,int root)172 void scatter(const communicator& comm, T& out_value, int root)
173 {
174   BOOST_ASSERT(comm.rank() != root);
175   detail::scatter_impl(comm, &out_value, 1, root, is_mpi_datatype<T>());
176 }
177 
178 template<typename T>
179 void
scatter(const communicator & comm,const T * in_values,T * out_values,int n,int root)180 scatter(const communicator& comm, const T* in_values, T* out_values, int n,
181         int root)
182 {
183   detail::scatter_impl(comm, in_values, out_values, n, root, is_mpi_datatype<T>());
184 }
185 
186 template<typename T>
187 void
scatter(const communicator & comm,const std::vector<T> & in_values,T * out_values,int n,int root)188 scatter(const communicator& comm, const std::vector<T>& in_values,
189         T* out_values, int n, int root)
190 {
191   ::boost::mpi::scatter(comm, &in_values[0], out_values, n, root);
192 }
193 
194 template<typename T>
scatter(const communicator & comm,T * out_values,int n,int root)195 void scatter(const communicator& comm, T* out_values, int n, int root)
196 {
197   BOOST_ASSERT(comm.rank() != root);
198   detail::scatter_impl(comm, out_values, n, root, is_mpi_datatype<T>());
199 }
200 
201 } } // end namespace boost::mpi
202 
203 #endif // BOOST_MPI_SCATTER_HPP
204