Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'depencencies' of https://framagit.org/simgrid/simgrid into depencencies
[simgrid.git] / src / smpi / colls / coll_tuned_topo.cpp
1 /* Copyright (c) 2013-2020. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /*
8  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
9  *                         University Research and Technology
10  *                         Corporation.  All rights reserved.
11  * Copyright (c) 2004-2005 The University of Tennessee and The University
12  *                         of Tennessee Research Foundation.  All rights
13  *                         reserved.
14  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
15  *                         University of Stuttgart.  All rights reserved.
16  * Copyright (c) 2004-2005 The Regents of the University of California.
17  *                         All rights reserved.
18  *
19  * Additional copyrights may follow
20  */
21
22 #include "coll_tuned_topo.hpp"
23 #include "colls_private.hpp"
24 /*
25  * Some static helpers.
26  */
27 static int pown( int fanout, int num )
28 {
29     int p = 1;
30     if( num < 0 ) return 0;
31     if (1==num) return fanout;
32     if (2==fanout) {
33         return p<<num;
34     }
35     else {
36       for (int j = 0; j < num; j++) {
37         p *= fanout;
38       }
39     }
40     return p;
41 }
42
43 static int calculate_level( int fanout, int rank )
44 {
45     int level, num;
46     if( rank < 0 ) return -1;
47     for( level = 0, num = 0; num <= rank; level++ ) {
48         num += pown(fanout, level);
49     }
50     return level-1;
51 }
52
53 static int calculate_num_nodes_up_to_level( int fanout, int level )
54 {
55     /* just use geometric progression formula for sum:
56        a^0+a^1+...a^(n-1) = (a^n-1)/(a-1) */
57     if (fanout > 1)
58       return ((pown(fanout, level) - 1) / (fanout - 1));
59     else
60       return 0; // is this right ?
61 }
62
63 /*
64  * And now the building functions.
65  *
66  * An example for fanout = 2, comm_size = 7
67  *
68  *              0           <-- delta = 1 (fanout^0)
69  *            /   \
70  *           1     2        <-- delta = 2 (fanout^1)
71  *          / \   / \
72  *         3   5 4   6      <-- delta = 4 (fanout^2)
73  */
74
75 ompi_coll_tree_t*
76 ompi_coll_tuned_topo_build_tree( int fanout,
77                                  MPI_Comm comm,
78                                  int root )
79 {
80     int rank, size;
81     int sparent;
82     int level; /* location of my rank in the tree structure of size */
83     int delta; /* number of nodes on my level */
84     int slimit; /* total number of nodes on levels above me */
85     int shiftedrank;
86     ompi_coll_tree_t* tree;
87
88     XBT_DEBUG("coll:tuned:topo_build_tree Building fo %d rt %d", fanout, root);
89
90     if (fanout<1) {
91         XBT_DEBUG("coll:tuned:topo_build_tree invalid fanout %d", fanout);
92         return NULL;
93     }
94     if (fanout>MAXTREEFANOUT) {
95         XBT_DEBUG("coll:tuned:topo_build_tree invalid fanout %d bigger than max %d", fanout, MAXTREEFANOUT);
96         return NULL;
97     }
98
99     /*
100      * Get size and rank of the process in this communicator
101      */
102     size = comm->size();
103     rank = comm->rank();
104
105     tree = new ompi_coll_tree_t;
106     if (not tree) {
107       XBT_DEBUG("coll:tuned:topo_build_tree PANIC::out of memory");
108       return NULL;
109     }
110
111     /*
112      * Initialize tree
113      */
114     tree->tree_fanout   = fanout;
115     tree->tree_bmtree   = 0;
116     tree->tree_root     = root;
117     tree->tree_prev     = -1;
118     tree->tree_nextsize = 0;
119     for (int i = 0; i < fanout; i++) {
120       tree->tree_next[i] = -1;
121     }
122
123     /* return if we have less than 2 processes */
124     if( size < 2 ) {
125         return tree;
126     }
127
128     /*
129      * Shift all ranks by root, so that the algorithm can be
130      * designed as if root would be always 0
131      * shiftedrank should be used in calculating distances
132      * and position in tree
133      */
134     shiftedrank = rank - root;
135     if( shiftedrank < 0 ) {
136         shiftedrank += size;
137     }
138
139     /* calculate my level */
140     level = calculate_level( fanout, shiftedrank );
141     delta = pown( fanout, level );
142
143     /* find my children */
144     for (int i = 0; i < fanout; i++) {
145         int schild = shiftedrank + delta * (i+1);
146         if( schild < size ) {
147             tree->tree_next[i] = (schild+root)%size;
148             tree->tree_nextsize = tree->tree_nextsize + 1;
149         } else {
150             break;
151         }
152     }
153
154     /* find my parent */
155     slimit = calculate_num_nodes_up_to_level( fanout, level );
156     sparent = shiftedrank;
157     if( sparent < fanout ) {
158         sparent = 0;
159     } else {
160         while( sparent >= slimit ) {
161             sparent -= delta/fanout;
162         }
163     }
164     tree->tree_prev = (sparent+root)%size;
165
166     return tree;
167 }
168
169 /*
170  * Constructs in-order binary tree which can be used for non-commutative reduce
171  * operations.
172  * Root of this tree is always rank (size-1) and fanout is 2.
173  * Here are some of the examples of this tree:
174  * size == 2     size == 3     size == 4                size == 9
175  *      1             2             3                        8
176  *     /             / \          /   \                    /   \
177  *    0             1  0         2     1                  7     3
178  *                                    /                 /  \   / \
179  *                                   0                 6    5 2   1
180  *                                                         /     /
181  *                                                        4     0
182  */
183 ompi_coll_tree_t*
184 ompi_coll_tuned_topo_build_in_order_bintree( MPI_Comm comm )
185 {
186     int rank, size;
187     int myrank, delta;
188     int parent;
189     ompi_coll_tree_t* tree;
190
191     /*
192      * Get size and rank of the process in this communicator
193      */
194     size = comm->size();
195     rank = comm->rank();
196
197     tree = new ompi_coll_tree_t;
198     if (not tree) {
199       XBT_DEBUG("coll:tuned:topo_build_tree PANIC::out of memory");
200       return NULL;
201     }
202
203     /*
204      * Initialize tree
205      */
206     tree->tree_fanout   = 2;
207     tree->tree_bmtree   = 0;
208     tree->tree_root     = size - 1;
209     tree->tree_prev     = -1;
210     tree->tree_nextsize = 0;
211     tree->tree_next[0]  = -1;
212     tree->tree_next[1]  = -1;
213     XBT_DEBUG(
214                  "coll:tuned:topo_build_in_order_tree Building fo %d rt %d",
215                  tree->tree_fanout, tree->tree_root);
216
217     /*
218      * Build the tree
219      */
220     myrank = rank;
221     parent = size - 1;
222     delta = 0;
223
224     while ( 1 ) {
225         /* Compute the size of the right subtree */
226         int rightsize = size >> 1;
227
228         /* Determine the left and right child of this parent  */
229         int lchild = -1;
230         int rchild = -1;
231         if (size - 1 > 0) {
232             lchild = parent - 1;
233             if (lchild > 0) {
234                 rchild = rightsize - 1;
235             }
236         }
237
238         /* The following cases are possible: myrank can be
239            - a parent,
240            - belong to the left subtree, or
241            - belong to the right subtee
242            Each of the cases need to be handled differently.
243         */
244
245         if (myrank == parent) {
246             /* I am the parent:
247                - compute real ranks of my children, and exit the loop. */
248             if (lchild >= 0) tree->tree_next[0] = lchild + delta;
249             if (rchild >= 0) tree->tree_next[1] = rchild + delta;
250             break;
251         }
252         if (myrank > rchild) {
253             /* I belong to the left subtree:
254                - If I am the left child, compute real rank of my parent
255                - Iterate down through tree:
256                compute new size, shift ranks down, and update delta.
257             */
258             if (myrank == lchild) {
259                 tree->tree_prev = parent + delta;
260             }
261             size = size - rightsize - 1;
262             delta = delta + rightsize;
263             myrank = myrank - rightsize;
264             parent = size - 1;
265
266         } else {
267             /* I belong to the right subtree:
268                - If I am the right child, compute real rank of my parent
269                - Iterate down through tree:
270                compute new size and parent,
271                but the delta and rank do not need to change.
272             */
273             if (myrank == rchild) {
274                 tree->tree_prev = parent + delta;
275             }
276             size = rightsize;
277             parent = rchild;
278         }
279     }
280
281     if (tree->tree_next[0] >= 0) { tree->tree_nextsize = 1; }
282     if (tree->tree_next[1] >= 0) { tree->tree_nextsize += 1; }
283
284     return tree;
285 }
286
287 int ompi_coll_tuned_topo_destroy_tree( ompi_coll_tree_t** tree )
288 {
289     ompi_coll_tree_t *ptr;
290
291     if ((tree == nullptr) || (*tree == nullptr)) {
292       return MPI_SUCCESS;
293     }
294
295     ptr = *tree;
296
297     delete ptr;
298     *tree = NULL;   /* mark tree as gone */
299
300     return MPI_SUCCESS;
301 }
302
303 /*
304  *
305  * Here are some of the examples of this tree:
306  * size == 2                   size = 4                 size = 8
307  *      0                           0                        0
308  *     /                            | \                    / | \
309  *    1                             2  1                  4  2  1
310  *                                     |                     |  |\
311  *                                     3                     6  5 3
312  *                                                                |
313  *                                                                7
314  */
315 ompi_coll_tree_t*
316 ompi_coll_tuned_topo_build_bmtree( MPI_Comm comm,
317                                    int root )
318 {
319     int children = 0;
320     int rank;
321     int size;
322     int mask = 1;
323     int index;
324     int remote;
325     ompi_coll_tree_t *bmtree;
326     int i;
327
328     XBT_DEBUG("coll:tuned:topo:build_bmtree rt %d", root);
329
330     /*
331      * Get size and rank of the process in this communicator
332      */
333     size = comm->size();
334     rank = comm->rank();
335
336     index = rank -root;
337
338     bmtree = new ompi_coll_tree_t;
339     if (not bmtree) {
340       XBT_DEBUG("coll:tuned:topo:build_bmtree PANIC out of memory");
341       return NULL;
342     }
343
344     bmtree->tree_bmtree   = 1;
345
346     bmtree->tree_root     = MPI_UNDEFINED;
347     bmtree->tree_nextsize = MPI_UNDEFINED;
348     for(i=0;i<MAXTREEFANOUT;i++) {
349         bmtree->tree_next[i] = -1;
350     }
351
352     if( index < 0 ) index += size;
353
354     while( mask <= index ) mask <<= 1;
355
356     /* Now I can compute my father rank */
357     if( root == rank ) {
358         bmtree->tree_prev = root;
359     } else {
360         remote = (index ^ (mask >> 1)) + root;
361         if( remote >= size ) remote -= size;
362         bmtree->tree_prev = remote;
363     }
364     /* And now let's fill my children */
365     while( mask < size ) {
366         remote = (index ^ mask);
367         if( remote >= size ) break;
368         remote += root;
369         if( remote >= size ) remote -= size;
370         if (children==MAXTREEFANOUT) {
371             XBT_DEBUG("coll:tuned:topo:build_bmtree max fanout incorrect %d needed %d", MAXTREEFANOUT, children);
372             delete bmtree;
373             return NULL;
374         }
375         bmtree->tree_next[children] = remote;
376         mask <<= 1;
377         children++;
378     }
379     bmtree->tree_nextsize = children;
380     bmtree->tree_root     = root;
381     return bmtree;
382 }
383
384 /*
385  * Constructs in-order binomial tree which can be used for gather/scatter
386  * operations.
387  *
388  * Here are some of the examples of this tree:
389  * size == 2                   size = 4                 size = 8
390  *      0                           0                        0
391  *     /                          / |                      / | \
392  *    1                          1  2                     1  2  4
393  *                                  |                        |  | \
394  *                                  3                        3  5  6
395  *                                                                 |
396  *                                                                 7
397  */
398 ompi_coll_tree_t* ompi_coll_tuned_topo_build_in_order_bmtree(MPI_Comm comm, int root)
399 {
400     int children = 0;
401     int rank, vrank;
402     int size;
403     int mask = 1;
404     ompi_coll_tree_t *bmtree;
405     int i;
406
407     XBT_DEBUG("coll:tuned:topo:build_in_order_bmtree rt %d", root);
408
409     /*
410      * Get size and rank of the process in this communicator
411      */
412     size = comm->size();
413     rank = comm->rank();
414
415     vrank = (rank - root + size) % size;
416
417     bmtree = new ompi_coll_tree_t;
418     if (not bmtree) {
419       XBT_DEBUG("coll:tuned:topo:build_bmtree PANIC out of memory");
420       delete bmtree;
421       return NULL;
422     }
423
424     bmtree->tree_bmtree   = 1;
425     bmtree->tree_root     = MPI_UNDEFINED;
426     bmtree->tree_nextsize = MPI_UNDEFINED;
427     for(i=0;i<MAXTREEFANOUT;i++) {
428         bmtree->tree_next[i] = -1;
429     }
430
431     if (root == rank) {
432       bmtree->tree_prev = root;
433     }
434
435     while (mask < size) {
436       int remote = vrank ^ mask;
437       if (remote < vrank) {
438         bmtree->tree_prev = (remote + root) % size;
439         break;
440       } else if (remote < size) {
441         bmtree->tree_next[children] = (remote + root) % size;
442         children++;
443         if (children == MAXTREEFANOUT) {
444           XBT_DEBUG("coll:tuned:topo:build_bmtree max fanout incorrect %d needed %d", MAXTREEFANOUT, children);
445           delete bmtree;
446           return NULL;
447         }
448       }
449       mask <<= 1;
450     }
451     bmtree->tree_nextsize = children;
452     bmtree->tree_root     = root;
453
454     return bmtree;
455 }
456
457
458 ompi_coll_tree_t*
459 ompi_coll_tuned_topo_build_chain( int fanout,
460                                   MPI_Comm comm,
461                                   int root )
462 {
463     int rank, size;
464     int srank; /* shifted rank */
465     int i,maxchainlen;
466     int mark;
467     ompi_coll_tree_t *chain;
468
469     XBT_DEBUG("coll:tuned:topo:build_chain fo %d rt %d", fanout, root);
470
471     /*
472      * Get size and rank of the process in this communicator
473      */
474     size = comm->size();
475     rank = comm->rank();
476
477     if( fanout < 1 ) {
478         XBT_DEBUG("coll:tuned:topo:build_chain WARNING invalid fanout of ZERO, forcing to 1 (pipeline)!");
479         fanout = 1;
480     }
481     if (fanout>MAXTREEFANOUT) {
482         XBT_DEBUG("coll:tuned:topo:build_chain WARNING invalid fanout %d bigger than max %d, forcing to max!", fanout, MAXTREEFANOUT);
483         fanout = MAXTREEFANOUT;
484     }
485
486     /*
487      * Allocate space for topology arrays if needed
488      */
489     chain = new ompi_coll_tree_t;
490     if (not chain) {
491       XBT_DEBUG("coll:tuned:topo:build_chain PANIC out of memory");
492       fflush(stdout);
493       return NULL;
494     }
495     for(i=0;i<fanout;i++) chain->tree_next[i] = -1;
496
497     /*
498      * Set root & numchain
499      */
500     chain->tree_root = root;
501     if( (size - 1) < fanout ) {
502         chain->tree_nextsize = size-1;
503         fanout = size-1;
504     } else {
505         chain->tree_nextsize = fanout;
506     }
507
508     /*
509      * Shift ranks
510      */
511     srank = rank - root;
512     if (srank < 0) srank += size;
513
514     /*
515      * Special case - fanout == 1
516      */
517     if( fanout == 1 ) {
518         if( srank == 0 ) chain->tree_prev = -1;
519         else chain->tree_prev = (srank-1+root)%size;
520
521         if( (srank + 1) >= size) {
522             chain->tree_next[0] = -1;
523             chain->tree_nextsize = 0;
524         } else {
525             chain->tree_next[0] = (srank+1+root)%size;
526             chain->tree_nextsize = 1;
527         }
528         return chain;
529     }
530
531     /* Let's handle the case where there is just one node in the communicator */
532     if( size == 1 ) {
533         chain->tree_next[0] = -1;
534         chain->tree_nextsize = 0;
535         chain->tree_prev = -1;
536         return chain;
537     }
538     /*
539      * Calculate maximum chain length
540      */
541     maxchainlen = (size-1) / fanout;
542     if( (size-1) % fanout != 0 ) {
543         maxchainlen++;
544         mark = (size-1)%fanout;
545     } else {
546         mark = fanout+1;
547     }
548
549     /*
550      * Find your own place in the list of shifted ranks
551      */
552     if( srank != 0 ) {
553         int column;
554         int head;
555         int len;
556         if( srank-1 < (mark * maxchainlen) ) {
557             column = (srank-1)/maxchainlen;
558             head = 1+column*maxchainlen;
559             len = maxchainlen;
560         } else {
561             column = mark + (srank-1-mark*maxchainlen)/(maxchainlen-1);
562             head = mark*maxchainlen+1+(column-mark)*(maxchainlen-1);
563             len = maxchainlen-1;
564         }
565
566         if( srank == head ) {
567             chain->tree_prev = 0; /*root*/
568         } else {
569             chain->tree_prev = srank-1; /* rank -1 */
570         }
571         if( srank == (head + len - 1) ) {
572             chain->tree_next[0] = -1;
573             chain->tree_nextsize = 0;
574         } else {
575             if( (srank + 1) < size ) {
576                 chain->tree_next[0] = srank+1;
577                 chain->tree_nextsize = 1;
578             } else {
579                 chain->tree_next[0] = -1;
580                 chain->tree_nextsize = 0;
581             }
582         }
583     }
584
585     /*
586      * Unshift values
587      */
588     if( rank == root ) {
589         chain->tree_prev = -1;
590         chain->tree_next[0] = (root+1)%size;
591         for (int i = 1; i < fanout; i++) {
592             chain->tree_next[i] = chain->tree_next[i-1] + maxchainlen;
593             if( i > mark ) {
594                 chain->tree_next[i]--;
595             }
596             chain->tree_next[i] %= size;
597         }
598         chain->tree_nextsize = fanout;
599     } else {
600         chain->tree_prev = (chain->tree_prev+root)%size;
601         if( chain->tree_next[0] != -1 ) {
602             chain->tree_next[0] = (chain->tree_next[0]+root)%size;
603         }
604     }
605
606     return chain;
607 }
608
609 int ompi_coll_tuned_topo_dump_tree (ompi_coll_tree_t* tree, int rank)
610 {
611     XBT_DEBUG("coll:tuned:topo:topo_dump_tree %1d tree root %d"
612                  " fanout %d BM %1d nextsize %d prev %d",
613                  rank, tree->tree_root, tree->tree_bmtree, tree->tree_fanout,
614                  tree->tree_nextsize, tree->tree_prev);
615     if( tree->tree_nextsize ) {
616       for (int i = 0; i < tree->tree_nextsize; i++)
617         XBT_DEBUG("[%1d] %d", i, tree->tree_next[i]);
618     }
619     return (0);
620 }