Logo AND Algorithmique Numérique Distribuée

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