Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' into CRTP
[simgrid.git] / src / smpi / mpi / smpi_topo.cpp
1 /* Copyright (c) 2014-2019. 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 #include "smpi/smpi.h"
7 #include "private.hpp"
8 #include "smpi_comm.hpp"
9 #include "smpi_topo.hpp"
10 #include "xbt/sysdep.h"
11 #include <cmath>
12 #include <vector>
13
14 /* static functions */
15 static int assignnodes(int ndim, int nfactor, int *pfacts,int **pdims);
16 static int getfactors(int num, int *nfators, int **factors);
17
18
19 namespace simgrid{
20 namespace smpi{
21
22 /*******************************************************************************
23  * Cartesian topologies
24  ******************************************************************************/
25
26 Topo_Cart::Topo_Cart(int ndims) : ndims_(ndims), dims_(ndims), periodic_(ndims), position_(ndims)
27 {
28 }
29
30 /* reorder is ignored, don't know what would be the consequences of a dumb reordering but neither do I see the point of
31  * reordering*/
32 Topo_Cart::Topo_Cart(MPI_Comm comm_old, int ndims, const int dims[], const int periods[], int /*reorder*/, MPI_Comm* comm_cart)
33     : Topo_Cart(ndims)
34 {
35   MPI_Group newGroup;
36   MPI_Group oldGroup;
37
38   int rank = comm_old->rank();
39
40   if(ndims != 0) {
41     int newSize = 1;
42     for (int i = 0 ; i < ndims ; i++) {
43       newSize *= dims[i];
44     }
45     if(rank >= newSize) {
46       *comm_cart = MPI_COMM_NULL;
47       return;
48     }
49     oldGroup = comm_old->group();
50     newGroup = new  Group(newSize);
51     for (int i = 0 ; i < newSize ; i++) {
52       newGroup->set_mapping(oldGroup->actor(i), i);
53     }
54
55     nnodes_ = newSize;
56
57     //  FIXME : code duplication... See coords
58     int nranks = newSize;
59     for (int i=0; i<ndims; i++) {
60       dims_[i] = dims[i];
61      periodic_[i] = periods[i];
62       nranks = nranks / dims[i];
63       /* FIXME: nranks could be zero (?) */
64       position_[i] = rank / nranks;
65       rank = rank % nranks;
66     }
67
68     *comm_cart = new  Comm(newGroup, this);
69   } else {
70     if (rank == 0) {
71       *comm_cart = new  Comm(new  Group(MPI_COMM_SELF->group()), this);
72     } else {
73       *comm_cart = MPI_COMM_NULL;
74     }
75   }
76   setComm(*comm_cart);
77 }
78
79 Topo_Cart* Topo_Cart::sub(const int remain_dims[], MPI_Comm *newcomm) {
80   int oldNDims = ndims_;
81   int *newDims = nullptr;
82   int *newPeriodic = nullptr;
83
84   if (remain_dims == nullptr && oldNDims != 0) {
85     return nullptr;
86   }
87   int newNDims = 0;
88   for (int i = 0 ; i < oldNDims ; i++) {
89     if (remain_dims[i])
90       newNDims++;
91   }
92
93   if (newNDims > 0) {
94     newDims     = new int[newNDims];
95     newPeriodic = new int[newNDims];
96
97     // that should not segfault
98     int j = 0;
99     for (int i = 0 ; j < newNDims ; i++) {
100       if(remain_dims[i]) {
101         newDims[j] =dims_[i];
102         newPeriodic[j] =periodic_[i];
103         j++;
104       }
105     }
106   }
107   Topo_Cart* res = new Topo_Cart(getComm(), newNDims, newDims, newPeriodic, 0, newcomm);
108   delete[] newDims;
109   delete[] newPeriodic;
110   return res;
111 }
112
113 int Topo_Cart::coords(int rank, int /*maxdims*/, int coords[])
114 {
115   int nnodes = nnodes_;
116   for (int i = 0; i< ndims_; i++ ) {
117     nnodes    = nnodes /dims_[i];
118     coords[i] = rank / nnodes;
119     rank      = rank % nnodes;
120   }
121   return MPI_SUCCESS;
122 }
123
124 int Topo_Cart::get(int maxdims, int* dims, int* periods, int* coords) {
125   int ndims=ndims_ < maxdims ?ndims_ : maxdims;
126   for(int i = 0 ; i < ndims ; i++) {
127     dims[i] =dims_[i];
128     periods[i] =periodic_[i];
129     coords[i] =position_[i];
130   }
131   return MPI_SUCCESS;
132 }
133
134 int Topo_Cart::rank(const int* coords, int* rank) {
135   int ndims =ndims_;
136   *rank = 0;
137   int multiplier = 1;
138
139   for (int i=ndims-1; i >=0; i-- ) {
140     int coord = coords[i];
141
142     /* The user can give us whatever coordinates he wants. If one of them is out of range, either this dimension is
143      * periodic, and we consider the equivalent coordinate inside the bounds, or it's not and then it's an error
144      */
145     if (coord >=dims_[i]) {
146       if (periodic_[i] ) {
147         coord = coord %dims_[i];
148       } else {
149         // Should I do that ?
150         *rank = -1;
151         return MPI_ERR_ARG;
152       }
153     } else if (coord <  0) {
154       if(periodic_[i]) {
155         coord = coord %dims_[i];
156         if (coord)
157           coord =dims_[i] + coord;
158       } else {
159         *rank = -1;
160         return MPI_ERR_ARG;
161       }
162     }
163
164     *rank += multiplier * coord;
165     multiplier *=dims_[i];
166   }
167   return MPI_SUCCESS;
168 }
169
170 int Topo_Cart::shift(int direction, int disp, int *rank_source, int *rank_dest) {
171
172   int position[ndims_];
173
174   if(ndims_ == 0) {
175     return MPI_ERR_ARG;
176   }
177   if (ndims_ < direction) {
178     return MPI_ERR_DIMS;
179   }
180
181   this->coords(getComm()->rank(), ndims_, position);
182   position[direction] += disp;
183
184   if(position[direction] < 0 ||
185       position[direction] >=dims_[direction]) {
186     if(periodic_[direction]) {
187       position[direction] %=dims_[direction];
188       this->rank(position, rank_dest);
189     } else {
190       *rank_dest = MPI_PROC_NULL;
191     }
192   } else {
193     this->rank(position, rank_dest);
194   }
195
196   position[direction] = position_[direction] - disp;
197   if(position[direction] < 0 || position[direction] >=dims_[direction]) {
198     if(periodic_[direction]) {
199       position[direction] %=dims_[direction];
200       this->rank(position, rank_source);
201     } else {
202       *rank_source = MPI_PROC_NULL;
203     }
204   } else {
205     this->rank(position, rank_source);
206   }
207
208   return MPI_SUCCESS;
209 }
210
211 int Topo_Cart::dim_get(int *ndims) {
212   *ndims =ndims_;
213   return MPI_SUCCESS;
214 }
215
216 // Everything below has been taken from ompi, but could be easily rewritten (and partially was to follow sonar rules).
217
218 /*
219  * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
220  *                         University Research and Technology
221  *                         Corporation.  All rights reserved.
222  * Copyright (c) 2004-2005 The University of Tennessee and The University
223  *                         of Tennessee Research Foundation.  All rights
224  *                         reserved.
225  * Copyright (c) 2004-2014 High Performance Computing Center Stuttgart,
226  *                         University of Stuttgart.  All rights reserved.
227  * Copyright (c) 2004-2005 The Regents of the University of California.
228  *                         All rights reserved.
229  * Copyright (c) 2012      Los Alamos National Security, LLC.  All rights
230  *                         reserved.
231  * Copyright (c) 2014      Intel, Inc. All rights reserved
232  * $COPYRIGHT$
233  *
234  * Additional copyrights may follow
235  *
236  * $HEADER$
237  */
238
239 /*
240  * This is a utility function, no need to have anything in the lower layer for this at all
241  */
242 int Topo_Cart::Dims_create(int nnodes, int ndims, int dims[])
243 {
244   /* Get # of free-to-be-assigned processes and # of free dimensions */
245   int freeprocs = nnodes;
246   int freedims = 0;
247   int *p = dims;
248   for (int i = 0; i < ndims; ++i) {
249     if (*p == 0) {
250       ++freedims;
251     } else if ((*p < 0) || ((nnodes % *p) != 0)) {
252       return  MPI_ERR_DIMS;
253
254     } else {
255       freeprocs /= *p;
256     }
257     p++;
258   }
259
260   if (freedims == 0) {
261     if (freeprocs == 1) {
262       return MPI_SUCCESS;
263     }
264     return MPI_ERR_DIMS;
265   }
266
267   if (freeprocs == 1) {
268     for (int i = 0; i < ndims; ++i) {
269       if (*dims == 0) {
270         *dims = 1;
271       }
272       dims++;
273     }
274     return MPI_SUCCESS;
275   }
276
277   /* Factor the number of free processes */
278   int nfactors;
279   int *factors;
280   int err = getfactors(freeprocs, &nfactors, &factors);
281   if (MPI_SUCCESS != err)
282     return  err;
283
284   /* Assign free processes to free dimensions */
285   int *procs;
286   err = assignnodes(freedims, nfactors, factors, &procs);
287   if (MPI_SUCCESS != err)
288     return err;
289
290   /* Return assignment results */
291   p = procs;
292   for (int i = 0; i < ndims; ++i) {
293     if (*dims == 0) {
294       *dims = *p++;
295     }
296     dims++;
297   }
298
299   delete[] factors;
300   delete[] procs;
301
302   /* all done */
303   return MPI_SUCCESS;
304 }
305
306 }
307 }
308
309 /*
310  *  assignnodes
311  *
312  *  Function:   - assign processes to dimensions
313  *          - get "best-balanced" grid
314  *          - greedy bin-packing algorithm used
315  *          - sort dimensions in decreasing order
316  *          - dimensions array dynamically allocated
317  *  Accepts:    - # of dimensions
318  *          - # of prime factors
319  *          - array of prime factors
320  *          - ptr to array of dimensions (returned value)
321  *  Returns:    - 0 or ERROR
322  */
323 static int assignnodes(int ndim, int nfactor, int *pfacts, int **pdims)
324 {
325   int *pmin;
326
327   if (0 >= ndim) {
328     return MPI_ERR_DIMS;
329   }
330
331   /* Allocate and initialize the bins */
332   int *bins = new int[ndim];
333
334   *pdims = bins;
335   int *p = bins;
336
337   for (int i = 0 ; i < ndim; ++i) {
338     *p = 1;
339     p++;
340   }
341   /* Loop assigning factors from the highest to the lowest */
342   for (int j = nfactor - 1; j >= 0; --j) {
343     int f = pfacts[j];
344     /* Assign a factor to the smallest bin */
345     pmin = bins;
346     p = pmin + 1;
347     for (int i = 1; i < ndim; ++i) {
348       if (*p < *pmin) {
349         pmin = p;
350       }
351       p++;
352     }
353     *pmin *= f;
354   }
355
356   /* Sort dimensions in decreasing order (O(n^2) for now) */
357   pmin = bins;
358   for (int i = 0; i < ndim - 1; ++i) {
359     p = pmin + 1;
360     for (int j = i + 1; j < ndim; ++j) {
361       if (*p > *pmin) {
362         int n = *p;
363         *p = *pmin;
364         *pmin = n;
365       }
366       p++;
367     }
368     pmin++;
369   }
370
371   return MPI_SUCCESS;
372 }
373
374 /*
375  *  getfactors
376  *
377  *  Function:   - factorize a number
378  *  Accepts:    - number
379  *          - # prime factors
380  *          - array of prime factors
381  *  Returns:    - MPI_SUCCESS or ERROR
382  */
383 static int getfactors(int num, int *nfactors, int **factors) {
384   if(num  < 2) {
385     (*nfactors) = 0;
386     (*factors) = nullptr;
387     return MPI_SUCCESS;
388   }
389   /* Allocate the array of prime factors which cannot exceed log_2(num) entries */
390   int sqrtnum = ceil(sqrt(double(num)));
391   int size = ceil(log(double(num)) / log(2.0));
392   *factors = new int[size];
393
394   int i = 0;
395   /* determine all occurrences of factor 2 */
396   while((num % 2) == 0) {
397     num /= 2;
398     (*factors)[i++] = 2;
399   }
400   /* determine all occurrences of uneven prime numbers up to sqrt(num) */
401   for(int d = 3; (num > 1) && (d < sqrtnum); d += 2) {
402     while((num % d) == 0) {
403       num /= d;
404       (*factors)[i++] = d;
405     }
406   }
407   /* as we looped only up to sqrt(num) one factor > sqrt(num) may be left over */
408   if(num != 1) {
409     (*factors)[i++] = num;
410   }
411   (*nfactors) = i;
412   return MPI_SUCCESS;
413 }
414