Logo AND Algorithmique Numérique Distribuée

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