Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Update copyright lines.
[simgrid.git] / src / smpi / colls / allgatherv / allgatherv-mpich-rdb.cpp
1 /* Copyright (c) 2013-2021. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 /* Short or medium size message and power-of-two no. of processes. Use
7  * recursive doubling algorithm */
8
9 #include "../colls_private.hpp"
10 #include "smpi_status.hpp"
11 #include <algorithm>
12
13 namespace simgrid{
14 namespace smpi{
15
16 int allgatherv__mpich_rdb(
17   const void *sendbuf,
18   int sendcount,
19   MPI_Datatype sendtype,
20   void *recvbuf,
21   const int *recvcounts,
22   const int *displs,
23   MPI_Datatype recvtype,
24   MPI_Comm comm)
25 {
26   unsigned int  j, i;
27   MPI_Status status;
28   MPI_Aint  recvtype_extent, recvtype_true_extent, recvtype_true_lb;
29   unsigned int curr_cnt, dst, total_count;
30   unsigned int mask, dst_tree_root, my_tree_root, position,
31     send_offset, recv_offset, last_recv_cnt=0, nprocs_completed, k,
32     offset, tmp_mask, tree_root;
33
34   unsigned int comm_size = comm->size();
35   unsigned int rank = comm->rank();
36
37   total_count = 0;
38   for (i=0; i<comm_size; i++)
39     total_count += recvcounts[i];
40
41   if (total_count == 0)
42     return MPI_ERR_COUNT;
43
44   recvtype_extent=recvtype->get_extent();
45
46   /* need to receive contiguously into tmp_buf because
47      displs could make the recvbuf noncontiguous */
48
49   recvtype->extent(&recvtype_true_lb, &recvtype_true_extent);
50
51   unsigned char* tmp_buf_rl = smpi_get_tmp_sendbuffer(total_count * std::max(recvtype_true_extent, recvtype_extent));
52
53   /* adjust for potential negative lower bound in datatype */
54   unsigned char* tmp_buf = tmp_buf_rl - recvtype_true_lb;
55
56   /* copy local data into right location in tmp_buf */
57   position = 0;
58   for (i=0; i<rank; i++)
59     position += recvcounts[i];
60   if (sendbuf != MPI_IN_PLACE)
61   {
62     Datatype::copy(sendbuf, sendcount, sendtype, tmp_buf + position * recvtype_extent, recvcounts[rank], recvtype);
63   }
64   else
65   {
66     /* if in_place specified, local data is found in recvbuf */
67     Datatype::copy(static_cast<char*>(recvbuf) + displs[rank] * recvtype_extent, recvcounts[rank], recvtype,
68                    tmp_buf + position * recvtype_extent, recvcounts[rank], recvtype);
69   }
70   curr_cnt = recvcounts[rank];
71
72   mask = 0x1;
73   i = 0;
74   while (mask < comm_size) {
75     dst = rank ^ mask;
76
77     /* find offset into send and recv buffers. zero out
78        the least significant "i" bits of rank and dst to
79        find root of src and dst subtrees. Use ranks of
80        roots as index to send from and recv into buffer */
81
82     dst_tree_root = dst >> i;
83     dst_tree_root <<= i;
84
85     my_tree_root = rank >> i;
86     my_tree_root <<= i;
87
88     if (dst < comm_size) {
89       send_offset = 0;
90       for (j=0; j<my_tree_root; j++)
91         send_offset += recvcounts[j];
92
93       recv_offset = 0;
94       for (j=0; j<dst_tree_root; j++)
95         recv_offset += recvcounts[j];
96
97       Request::sendrecv(tmp_buf + send_offset * recvtype_extent, curr_cnt, recvtype, dst, COLL_TAG_ALLGATHERV,
98                         tmp_buf + recv_offset * recvtype_extent, total_count - recv_offset, recvtype, dst,
99                         COLL_TAG_ALLGATHERV, comm, &status);
100       /* for convenience, recv is posted for a bigger amount
101          than will be sent */
102       last_recv_cnt=Status::get_count(&status, recvtype);
103       curr_cnt += last_recv_cnt;
104     }
105
106     /* if some processes in this process's subtree in this step
107        did not have any destination process to communicate with
108        because of non-power-of-two, we need to send them the
109        data that they would normally have received from those
110        processes. That is, the haves in this subtree must send to
111        the havenots. We use a logarithmic
112        recursive-halfing algorithm for this. */
113
114     /* This part of the code will not currently be
115        executed because we are not using recursive
116        doubling for non power of two. Mark it as experimental
117        so that it doesn't show up as red in the coverage
118        tests. */
119
120     /* --BEGIN EXPERIMENTAL-- */
121     if (dst_tree_root + mask > comm_size) {
122       nprocs_completed = comm_size - my_tree_root - mask;
123       /* nprocs_completed is the number of processes in this
124          subtree that have all the data. Send data to others
125          in a tree fashion. First find root of current tree
126          that is being divided into two. k is the number of
127          least-significant bits in this process's rank that
128          must be zeroed out to find the rank of the root */
129       j = mask;
130       k = 0;
131       while (j) {
132         j >>= 1;
133         k++;
134       }
135       k--;
136
137       tmp_mask = mask >> 1;
138
139       while (tmp_mask) {
140         dst = rank ^ tmp_mask;
141
142         tree_root = rank >> k;
143         tree_root <<= k;
144
145         /* send only if this proc has data and destination
146            doesn't have data. at any step, multiple processes
147            can send if they have the data */
148         if ((dst > rank) &&
149             (rank < tree_root + nprocs_completed)
150             && (dst >= tree_root + nprocs_completed)) {
151
152           offset = 0;
153           for (j=0; j<(my_tree_root+mask); j++)
154             offset += recvcounts[j];
155           offset *= recvtype_extent;
156
157           Request::send(tmp_buf + offset, last_recv_cnt, recvtype, dst, COLL_TAG_ALLGATHERV, comm);
158           /* last_recv_cnt was set in the previous
159              receive. that's the amount of data to be
160              sent now. */
161         }
162         /* recv only if this proc. doesn't have data and sender
163            has data */
164         else if ((dst < rank) &&
165                  (dst < tree_root + nprocs_completed) &&
166                  (rank >= tree_root + nprocs_completed)) {
167
168           offset = 0;
169           for (j=0; j<(my_tree_root+mask); j++)
170             offset += recvcounts[j];
171
172           Request::recv(tmp_buf + offset * recvtype_extent, total_count - offset, recvtype, dst, COLL_TAG_ALLGATHERV,
173                         comm, &status);
174           /* for convenience, recv is posted for a
175              bigger amount than will be sent */
176           last_recv_cnt=Status::get_count(&status, recvtype);
177           curr_cnt += last_recv_cnt;
178         }
179         tmp_mask >>= 1;
180         k--;
181       }
182     }
183     /* --END EXPERIMENTAL-- */
184
185     mask <<= 1;
186     i++;
187   }
188
189   /* copy data from tmp_buf to recvbuf */
190   position = 0;
191   for (j=0; j<comm_size; j++) {
192     if ((sendbuf != MPI_IN_PLACE) || (j != rank)) {
193       /* not necessary to copy if in_place and
194          j==rank. otherwise copy. */
195       Datatype::copy(tmp_buf + position * recvtype_extent, recvcounts[j], recvtype,
196                      static_cast<char*>(recvbuf) + displs[j] * recvtype_extent, recvcounts[j], recvtype);
197     }
198     position += recvcounts[j];
199   }
200
201   smpi_free_tmp_buffer(tmp_buf_rl);
202   return MPI_SUCCESS;
203 }
204
205 }
206 }