1 /* Copyright (c) 2014-2020. The SimGrid Team. All rights reserved. */
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. */
8 #include "smpi_comm.hpp"
9 #include "smpi_topo.hpp"
10 #include "xbt/sysdep.h"
14 /* static functions */
15 static int assignnodes(int ndim, int nfactor, const int* pfacts, int** pdims);
16 static int getfactors(int num, int *nfators, int **factors);
22 void Topo::setComm(MPI_Comm comm)
24 xbt_assert(not comm_);
28 /*******************************************************************************
29 * Cartesian topologies
30 ******************************************************************************/
32 Topo_Cart::Topo_Cart(int ndims) : ndims_(ndims), dims_(ndims), periodic_(ndims), position_(ndims)
36 /* reorder is ignored, don't know what would be the consequences of a dumb reordering but neither do I see the point of
38 Topo_Cart::Topo_Cart(MPI_Comm comm_old, int ndims, const int dims[], const int periods[], int /*reorder*/, MPI_Comm* comm_cart)
44 int rank = comm_old->rank();
48 for (int i = 0 ; i < ndims ; i++) {
52 if(comm_cart != nullptr)
53 *comm_cart = MPI_COMM_NULL;
59 // FIXME : code duplication... See coords
61 for (int i=0; i<ndims; i++) {
63 periodic_[i] = periods[i];
64 nranks = nranks / dims[i];
65 /* FIXME: nranks could be zero (?) */
66 position_[i] = rank / nranks;
70 if(comm_cart != nullptr){
71 oldGroup = comm_old->group();
72 newGroup = new Group(newSize);
73 for (int i = 0 ; i < newSize ; i++) {
74 newGroup->set_mapping(oldGroup->actor(i), i);
76 *comm_cart = new Comm(newGroup, std::shared_ptr<Topo>(this));
79 if(comm_cart != nullptr){
81 MPI_Group group = new Group(MPI_COMM_SELF->group());
82 *comm_cart = new Comm(group, std::shared_ptr<Topo>(this));
84 *comm_cart = MPI_COMM_NULL;
88 if(comm_cart != nullptr){
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;
98 if (remain_dims == nullptr && oldNDims != 0) {
102 for (int i = 0 ; i < oldNDims ; i++) {
108 newDims = new int[newNDims];
109 newPeriodic = new int[newNDims];
111 // that should not segfault
113 for (int i = 0; i < oldNDims; i++) {
115 newDims[j] =dims_[i];
116 newPeriodic[j] =periodic_[i];
122 //split into several communicators
124 for (int i = 0; i < oldNDims; i++) {
125 if (not remain_dims[i]) {
126 color = (color * dims_[i] + position_[i]);
131 res = new Topo_Cart(getComm(), newNDims, newDims, newPeriodic, 0, newcomm);
133 *newcomm = getComm()->split(color, getComm()->rank());
134 res = new Topo_Cart(getComm(), newNDims, newDims, newPeriodic, 0, nullptr);
135 std::shared_ptr<Topo> topo=std::shared_ptr<Topo>(res);
136 res->setComm(*newcomm);
137 (*newcomm)->set_topo(topo);
140 delete[] newPeriodic;
144 int Topo_Cart::coords(int rank, int /*maxdims*/, int coords[])
146 int nnodes = nnodes_;
147 for (int i = 0; i< ndims_; i++ ) {
148 nnodes = nnodes /dims_[i];
149 coords[i] = rank / nnodes;
150 rank = rank % nnodes;
155 int Topo_Cart::get(int maxdims, int* dims, int* periods, int* coords) {
156 int ndims=ndims_ < maxdims ?ndims_ : maxdims;
157 for(int i = 0 ; i < ndims ; i++) {
159 periods[i] =periodic_[i];
160 coords[i] =position_[i];
165 int Topo_Cart::rank(const int* coords, int* rank) {
170 for (int i=ndims-1; i >=0; i-- ) {
171 int coord = coords[i];
173 /* The user can give us whatever coordinates he wants. If one of them is out of range, either this dimension is
174 * periodic, and we consider the equivalent coordinate inside the bounds, or it's not and then it's an error
176 if (coord >=dims_[i]) {
178 coord = coord %dims_[i];
180 // Should I do that ?
184 } else if (coord < 0) {
186 coord = coord %dims_[i];
188 coord =dims_[i] + coord;
195 *rank += multiplier * coord;
196 multiplier *=dims_[i];
201 int Topo_Cart::shift(int direction, int disp, int* rank_source, int* rank_dest)
206 if (ndims_ < direction) {
210 int* position = new int[ndims_];
211 this->coords(getComm()->rank(), ndims_, position);
212 position[direction] += disp;
214 if(position[direction] < 0 ||
215 position[direction] >=dims_[direction]) {
216 if(periodic_[direction]) {
217 position[direction] %=dims_[direction];
218 this->rank(position, rank_dest);
220 *rank_dest = MPI_PROC_NULL;
223 this->rank(position, rank_dest);
226 position[direction] = position_[direction] - disp;
227 if(position[direction] < 0 || position[direction] >=dims_[direction]) {
228 if(periodic_[direction]) {
229 position[direction] %=dims_[direction];
230 this->rank(position, rank_source);
232 *rank_source = MPI_PROC_NULL;
235 this->rank(position, rank_source);
241 int Topo_Cart::dim_get(int *ndims) {
246 // Everything below has been taken from ompi, but could be easily rewritten (and partially was to follow sonar rules).
249 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
250 * University Research and Technology
251 * Corporation. All rights reserved.
252 * Copyright (c) 2004-2005 The University of Tennessee and The University
253 * of Tennessee Research Foundation. All rights
255 * Copyright (c) 2004-2014 High Performance Computing Center Stuttgart,
256 * University of Stuttgart. All rights reserved.
257 * Copyright (c) 2004-2005 The Regents of the University of California.
258 * All rights reserved.
259 * Copyright (c) 2012 Los Alamos National Security, LLC. All rights
261 * Copyright (c) 2014 Intel, Inc. All rights reserved
264 * Additional copyrights may follow
270 * This is a utility function, no need to have anything in the lower layer for this at all
272 int Topo_Cart::Dims_create(int nnodes, int ndims, int dims[])
274 /* Get # of free-to-be-assigned processes and # of free dimensions */
275 int freeprocs = nnodes;
278 for (int i = 0; i < ndims; ++i) {
281 } else if ((*p < 0) || ((nnodes % *p) != 0)) {
291 if (freeprocs == 1) {
297 if (freeprocs == 1) {
298 for (int i = 0; i < ndims; ++i) {
307 /* Factor the number of free processes */
310 int err = getfactors(freeprocs, &nfactors, &factors);
311 if (MPI_SUCCESS != err)
314 /* Assign free processes to free dimensions */
316 err = assignnodes(freedims, nfactors, factors, &procs);
317 if (MPI_SUCCESS != err)
320 /* Return assignment results */
322 for (int i = 0; i < ndims; ++i) {
342 * Function: - assign processes to dimensions
343 * - get "best-balanced" grid
344 * - greedy bin-packing algorithm used
345 * - sort dimensions in decreasing order
346 * - dimensions array dynamically allocated
347 * Accepts: - # of dimensions
348 * - # of prime factors
349 * - array of prime factors
350 * - ptr to array of dimensions (returned value)
351 * Returns: - 0 or ERROR
353 static int assignnodes(int ndim, int nfactor, const int* pfacts, int** pdims)
361 /* Allocate and initialize the bins */
362 int *bins = new int[ndim];
367 for (int i = 0 ; i < ndim; ++i) {
371 /* Loop assigning factors from the highest to the lowest */
372 for (int j = nfactor - 1; j >= 0; --j) {
374 /* Assign a factor to the smallest bin */
377 for (int i = 1; i < ndim; ++i) {
386 /* Sort dimensions in decreasing order (O(n^2) for now) */
388 for (int i = 0; i < ndim - 1; ++i) {
390 for (int j = i + 1; j < ndim; ++j) {
407 * Function: - factorize a number
410 * - array of prime factors
411 * Returns: - MPI_SUCCESS or ERROR
413 static int getfactors(int num, int *nfactors, int **factors) {
416 (*factors) = nullptr;
419 /* Allocate the array of prime factors which cannot exceed log_2(num) entries */
420 int sqrtnum = ceil(sqrt(double(num)));
421 int size = ceil(log(double(num)) / log(2.0));
422 *factors = new int[size];
425 /* determine all occurrences of factor 2 */
426 while((num % 2) == 0) {
430 /* determine all occurrences of uneven prime numbers up to sqrt(num) */
431 for(int d = 3; (num > 1) && (d < sqrtnum); d += 2) {
432 while((num % d) == 0) {
437 /* as we looped only up to sqrt(num) one factor > sqrt(num) may be left over */
439 (*factors)[i++] = num;