Logo AND Algorithmique Numérique Distribuée

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