Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Adding integration tests of async-waitall and waitany
[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 ((tree == nullptr) || (*tree == nullptr)) {
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* ompi_coll_tuned_topo_build_in_order_bmtree(MPI_Comm comm, int root)
405 {
406     int childs = 0;
407     int rank, vrank;
408     int size;
409     int mask = 1;
410     int remote;
411     ompi_coll_tree_t *bmtree;
412     int i;
413
414     XBT_DEBUG("coll:tuned:topo:build_in_order_bmtree rt %d", root);
415
416     /*
417      * Get size and rank of the process in this communicator
418      */
419     size = comm->size();
420     rank = comm->rank();
421
422     vrank = (rank - root + size) % size;
423
424     bmtree = (ompi_coll_tree_t*)xbt_malloc(sizeof(ompi_coll_tree_t));
425     if (not bmtree) {
426       XBT_DEBUG("coll:tuned:topo:build_bmtree PANIC out of memory");
427       return NULL;
428     }
429
430     bmtree->tree_bmtree   = 1;
431     bmtree->tree_root     = MPI_UNDEFINED;
432     bmtree->tree_nextsize = MPI_UNDEFINED;
433     for(i=0;i<MAXTREEFANOUT;i++) {
434         bmtree->tree_next[i] = -1;
435     }
436
437     if (root == rank) {
438       bmtree->tree_prev = root;
439     }
440
441     while (mask < size) {
442       remote = vrank ^ mask;
443       if (remote < vrank) {
444         bmtree->tree_prev = (remote + root) % size;
445         break;
446       } else if (remote < size) {
447         bmtree->tree_next[childs] = (remote + root) % size;
448         childs++;
449         if (childs == MAXTREEFANOUT) {
450           XBT_DEBUG("coll:tuned:topo:build_bmtree max fanout incorrect %d needed %d", MAXTREEFANOUT, childs);
451           return NULL;
452         }
453       }
454       mask <<= 1;
455     }
456     bmtree->tree_nextsize = childs;
457     bmtree->tree_root     = root;
458
459     return bmtree;
460 }
461
462
463 ompi_coll_tree_t*
464 ompi_coll_tuned_topo_build_chain( int fanout,
465                                   MPI_Comm comm,
466                                   int root )
467 {
468     int rank, size;
469     int srank; /* shifted rank */
470     int i,maxchainlen;
471     int mark,head,len;
472     ompi_coll_tree_t *chain;
473
474     XBT_DEBUG("coll:tuned:topo:build_chain fo %d rt %d", fanout, root);
475
476     /*
477      * Get size and rank of the process in this communicator
478      */
479     size = comm->size();
480     rank = comm->rank();
481
482     if( fanout < 1 ) {
483         XBT_DEBUG("coll:tuned:topo:build_chain WARNING invalid fanout of ZERO, forcing to 1 (pipeline)!");
484         fanout = 1;
485     }
486     if (fanout>MAXTREEFANOUT) {
487         XBT_DEBUG("coll:tuned:topo:build_chain WARNING invalid fanout %d bigger than max %d, forcing to max!", fanout, MAXTREEFANOUT);
488         fanout = MAXTREEFANOUT;
489     }
490
491     /*
492      * Allocate space for topology arrays if needed
493      */
494     chain = (ompi_coll_tree_t*)malloc( sizeof(ompi_coll_tree_t) );
495     if (not chain) {
496       XBT_DEBUG("coll:tuned:topo:build_chain PANIC out of memory");
497       fflush(stdout);
498       return NULL;
499     }
500     chain->tree_root     = MPI_UNDEFINED;
501     chain->tree_nextsize = -1;
502     for(i=0;i<fanout;i++) chain->tree_next[i] = -1;
503
504     /*
505      * Set root & numchain
506      */
507     chain->tree_root = root;
508     if( (size - 1) < fanout ) {
509         chain->tree_nextsize = size-1;
510         fanout = size-1;
511     } else {
512         chain->tree_nextsize = fanout;
513     }
514
515     /*
516      * Shift ranks
517      */
518     srank = rank - root;
519     if (srank < 0) srank += size;
520
521     /*
522      * Special case - fanout == 1
523      */
524     if( fanout == 1 ) {
525         if( srank == 0 ) chain->tree_prev = -1;
526         else chain->tree_prev = (srank-1+root)%size;
527
528         if( (srank + 1) >= size) {
529             chain->tree_next[0] = -1;
530             chain->tree_nextsize = 0;
531         } else {
532             chain->tree_next[0] = (srank+1+root)%size;
533             chain->tree_nextsize = 1;
534         }
535         return chain;
536     }
537
538     /* Let's handle the case where there is just one node in the communicator */
539     if( size == 1 ) {
540         chain->tree_next[0] = -1;
541         chain->tree_nextsize = 0;
542         chain->tree_prev = -1;
543         return chain;
544     }
545     /*
546      * Calculate maximum chain length
547      */
548     maxchainlen = (size-1) / fanout;
549     if( (size-1) % fanout != 0 ) {
550         maxchainlen++;
551         mark = (size-1)%fanout;
552     } else {
553         mark = fanout+1;
554     }
555
556     /*
557      * Find your own place in the list of shifted ranks
558      */
559     if( srank != 0 ) {
560         int column;
561         if( srank-1 < (mark * maxchainlen) ) {
562             column = (srank-1)/maxchainlen;
563             head = 1+column*maxchainlen;
564             len = maxchainlen;
565         } else {
566             column = mark + (srank-1-mark*maxchainlen)/(maxchainlen-1);
567             head = mark*maxchainlen+1+(column-mark)*(maxchainlen-1);
568             len = maxchainlen-1;
569         }
570
571         if( srank == head ) {
572             chain->tree_prev = 0; /*root*/
573         } else {
574             chain->tree_prev = srank-1; /* rank -1 */
575         }
576         if( srank == (head + len - 1) ) {
577             chain->tree_next[0] = -1;
578             chain->tree_nextsize = 0;
579         } else {
580             if( (srank + 1) < size ) {
581                 chain->tree_next[0] = srank+1;
582                 chain->tree_nextsize = 1;
583             } else {
584                 chain->tree_next[0] = -1;
585                 chain->tree_nextsize = 0;
586             }
587         }
588     }
589
590     /*
591      * Unshift values
592      */
593     if( rank == root ) {
594         chain->tree_prev = -1;
595         chain->tree_next[0] = (root+1)%size;
596         for( i = 1; i < fanout; i++ ) {
597             chain->tree_next[i] = chain->tree_next[i-1] + maxchainlen;
598             if( i > mark ) {
599                 chain->tree_next[i]--;
600             }
601             chain->tree_next[i] %= size;
602         }
603         chain->tree_nextsize = fanout;
604     } else {
605         chain->tree_prev = (chain->tree_prev+root)%size;
606         if( chain->tree_next[0] != -1 ) {
607             chain->tree_next[0] = (chain->tree_next[0]+root)%size;
608         }
609     }
610
611     return chain;
612 }
613
614 int ompi_coll_tuned_topo_dump_tree (ompi_coll_tree_t* tree, int rank)
615 {
616     int i;
617
618     XBT_DEBUG("coll:tuned:topo:topo_dump_tree %1d tree root %d"
619                  " fanout %d BM %1d nextsize %d prev %d",
620                  rank, tree->tree_root, tree->tree_bmtree, tree->tree_fanout,
621                  tree->tree_nextsize, tree->tree_prev);
622     if( tree->tree_nextsize ) {
623         for( i = 0; i < tree->tree_nextsize; i++ )
624             XBT_DEBUG("[%1d] %d", i, tree->tree_next[i]);
625     }
626     return (0);
627 }