Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of git+ssh://scm.gforge.inria.fr//gitroot/simgrid/simgrid
[simgrid.git] / src / smpi / smpi_coll.c
1 /* smpi_coll.c -- various optimized routing for collectives                   */
2
3 /* Copyright (c) 2009, 2010. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include "private.h"
14 #include "colls/colls.h"
15
16 s_mpi_coll_description_t mpi_coll_alltoall_description[] = {
17   {"ompi",
18    "Ompi alltoall default collective",
19    smpi_coll_tuned_alltoall_ompi},
20
21   {"2dmesh",
22    "Alltoall 2dmesh collective",
23    smpi_coll_tuned_alltoall_2dmesh},
24   {"3dmesh",
25    "Alltoall 3dmesh collective",
26    smpi_coll_tuned_alltoall_3dmesh},
27   /*{"bruck",
28    "Alltoall Bruck collective",
29    smpi_coll_tuned_alltoall_bruck},*/
30   {"pair",
31    "Alltoall pair collective",
32    smpi_coll_tuned_alltoall_pair},
33   {"pair_light_barrier",
34    "Alltoall pair_light_barrier collective",
35    smpi_coll_tuned_alltoall_pair_light_barrier},
36   {"pair_mpi_barrier",
37    "Alltoall pair_mpi_barrier collective",
38    smpi_coll_tuned_alltoall_pair_mpi_barrier},
39   {"rdb",
40    "Alltoall rdb collective",
41    smpi_coll_tuned_alltoall_rdb},
42   {"ring",
43    "Alltoall ring collective",
44    smpi_coll_tuned_alltoall_ring},
45   {"ring_light_barrier",
46    "Alltoall ring_light_barrier collective",
47    smpi_coll_tuned_alltoall_ring_light_barrier},
48   {"ring_light_barrier",
49    "Alltoall ring_light_barrier collective",
50    smpi_coll_tuned_alltoall_ring_light_barrier},
51   {"ring_mpi_barrier",
52    "Alltoall ring_mpi_barrier collective",
53    smpi_coll_tuned_alltoall_ring_mpi_barrier},
54   {"ring_one_barrier",
55    "Alltoall ring_one_barrier collective",
56    smpi_coll_tuned_alltoall_ring_one_barrier},
57   {"simple",
58    "Alltoall simple collective",
59    smpi_coll_tuned_alltoall_simple},
60
61   {"bruck",
62    "Alltoall Bruck (SG) collective",
63    smpi_coll_tuned_alltoall_bruck},
64   {"basic_linear",
65    "Alltoall basic linear (SG) collective",
66    smpi_coll_tuned_alltoall_basic_linear},
67   {"pairwise",
68    "Alltoall pairwise (SG) collective",
69    smpi_coll_tuned_alltoall_pairwise},
70
71   {NULL, NULL, NULL}      /* this array must be NULL terminated */
72 };
73
74 s_mpi_coll_description_t mpi_coll_allgather_description[] = {
75   {"default",
76    "allgather default collective",
77    smpi_mpi_gather},
78
79   {NULL, NULL, NULL}      /* this array must be NULL terminated */
80 };
81
82
83 /** Displays the long description of all registered models, and quit */
84 void coll_help(const char *category, s_mpi_coll_description_t * table)
85 {
86   int i;
87   printf("Long description of the %s models accepted by this simulator:\n",
88          category);
89   for (i = 0; table[i].name; i++)
90     printf("  %s: %s\n", table[i].name, table[i].description);
91 }
92
93 int find_coll_description(s_mpi_coll_description_t * table,
94                            const char *name)
95 {
96   int i;
97   char *name_list = NULL;
98
99   for (i = 0; table[i].name; i++)
100     if (!strcmp(name, table[i].name)) {
101       return i;
102     }
103   name_list = strdup(table[0].name);
104   for (i = 1; table[i].name; i++) {
105     name_list =
106         xbt_realloc(name_list,
107                     strlen(name_list) + strlen(table[i].name) + 3);
108     strcat(name_list, ", ");
109     strcat(name_list, table[i].name);
110   }
111   xbt_die("Model '%s' is invalid! Valid models are: %s.", name, name_list);
112   return -1;
113 }
114
115
116
117 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_coll, smpi,
118                                 "Logging specific to SMPI (coll)");
119
120 struct s_proc_tree {
121   int PROCTREE_A;
122   int numChildren;
123   int *child;
124   int parent;
125   int me;
126   int root;
127   int isRoot;
128 };
129 typedef struct s_proc_tree *proc_tree_t;
130
131 /**
132  * alloc and init
133  **/
134 static proc_tree_t alloc_tree(int arity)
135 {
136   proc_tree_t tree;
137   int i;
138
139   tree = xbt_new(struct s_proc_tree, 1);
140   tree->PROCTREE_A = arity;
141   tree->isRoot = 0;
142   tree->numChildren = 0;
143   tree->child = xbt_new(int, arity);
144   for (i = 0; i < arity; i++) {
145     tree->child[i] = -1;
146   }
147   tree->root = -1;
148   tree->parent = -1;
149   return tree;
150 }
151
152 /**
153  * free
154  **/
155 static void free_tree(proc_tree_t tree)
156 {
157   xbt_free(tree->child);
158   xbt_free(tree);
159 }
160
161 /**
162  * Build the tree depending on a process rank (index) and the group size (extent)
163  * @param root the rank of the tree root
164  * @param rank the rank of the calling process
165  * @param size the total number of processes
166  **/
167 static void build_tree(int root, int rank, int size, proc_tree_t * tree)
168 {
169   int index = (rank - root + size) % size;
170   int firstChildIdx = index * (*tree)->PROCTREE_A + 1;
171   int i;
172
173   (*tree)->me = rank;
174   (*tree)->root = root;
175
176   for (i = 0; i < (*tree)->PROCTREE_A && firstChildIdx + i < size; i++) {
177     (*tree)->child[i] = (firstChildIdx + i + root) % size;
178     (*tree)->numChildren++;
179   }
180   if (rank == root) {
181     (*tree)->isRoot = 1;
182   } else {
183     (*tree)->isRoot = 0;
184     (*tree)->parent = (((index - 1) / (*tree)->PROCTREE_A) + root) % size;
185   }
186 }
187
188 /**
189  * bcast
190  **/
191 static void tree_bcast(void *buf, int count, MPI_Datatype datatype,
192                        MPI_Comm comm, proc_tree_t tree)
193 {
194   int system_tag = 999;         // used negative int but smpi_create_request() declares this illegal (to be checked)
195   int rank, i;
196   MPI_Request *requests;
197
198   rank = smpi_comm_rank(comm);
199   /* wait for data from my parent in the tree */
200   if (!tree->isRoot) {
201     XBT_DEBUG("<%d> tree_bcast(): i am not root: recv from %d, tag=%d)",
202            rank, tree->parent, system_tag + rank);
203     smpi_mpi_recv(buf, count, datatype, tree->parent, system_tag + rank,
204                   comm, MPI_STATUS_IGNORE);
205   }
206   requests = xbt_new(MPI_Request, tree->numChildren);
207   XBT_DEBUG("<%d> creates %d requests (1 per child)", rank,
208          tree->numChildren);
209   /* iniates sends to ranks lower in the tree */
210   for (i = 0; i < tree->numChildren; i++) {
211     if (tree->child[i] == -1) {
212       requests[i] = MPI_REQUEST_NULL;
213     } else {
214       XBT_DEBUG("<%d> send to <%d>, tag=%d", rank, tree->child[i],
215              system_tag + tree->child[i]);
216       requests[i] =
217           smpi_isend_init(buf, count, datatype, tree->child[i],
218                           system_tag + tree->child[i], comm);
219     }
220   }
221   smpi_mpi_startall(tree->numChildren, requests);
222   smpi_mpi_waitall(tree->numChildren, requests, MPI_STATUS_IGNORE);
223   xbt_free(requests);
224 }
225
226 /**
227  * anti-bcast
228  **/
229 static void tree_antibcast(void *buf, int count, MPI_Datatype datatype,
230                            MPI_Comm comm, proc_tree_t tree)
231 {
232   int system_tag = 999;         // used negative int but smpi_create_request() declares this illegal (to be checked)
233   int rank, i;
234   MPI_Request *requests;
235
236   rank = smpi_comm_rank(comm);
237   // everyone sends to its parent, except root.
238   if (!tree->isRoot) {
239     XBT_DEBUG("<%d> tree_antibcast(): i am not root: send to %d, tag=%d)",
240            rank, tree->parent, system_tag + rank);
241     smpi_mpi_send(buf, count, datatype, tree->parent, system_tag + rank,
242                   comm);
243   }
244   //every one receives as many messages as it has children
245   requests = xbt_new(MPI_Request, tree->numChildren);
246   XBT_DEBUG("<%d> creates %d requests (1 per child)", rank,
247          tree->numChildren);
248   for (i = 0; i < tree->numChildren; i++) {
249     if (tree->child[i] == -1) {
250       requests[i] = MPI_REQUEST_NULL;
251     } else {
252       XBT_DEBUG("<%d> recv from <%d>, tag=%d", rank, tree->child[i],
253              system_tag + tree->child[i]);
254       requests[i] =
255           smpi_irecv_init(buf, count, datatype, tree->child[i],
256                           system_tag + tree->child[i], comm);
257     }
258   }
259   smpi_mpi_startall(tree->numChildren, requests);
260   smpi_mpi_waitall(tree->numChildren, requests, MPI_STATUS_IGNORE);
261   xbt_free(requests);
262 }
263
264 /**
265  * bcast with a binary, ternary, or whatever tree ..
266  **/
267 void nary_tree_bcast(void *buf, int count, MPI_Datatype datatype, int root,
268                      MPI_Comm comm, int arity)
269 {
270   proc_tree_t tree = alloc_tree(arity);
271   int rank, size;
272
273   rank = smpi_comm_rank(comm);
274   size = smpi_comm_size(comm);
275   build_tree(root, rank, size, &tree);
276   tree_bcast(buf, count, datatype, comm, tree);
277   free_tree(tree);
278 }
279
280 /**
281  * barrier with a binary, ternary, or whatever tree ..
282  **/
283 void nary_tree_barrier(MPI_Comm comm, int arity)
284 {
285   proc_tree_t tree = alloc_tree(arity);
286   int rank, size;
287   char dummy = '$';
288
289   rank = smpi_comm_rank(comm);
290   size = smpi_comm_size(comm);
291   build_tree(0, rank, size, &tree);
292   tree_antibcast(&dummy, 1, MPI_CHAR, comm, tree);
293   tree_bcast(&dummy, 1, MPI_CHAR, comm, tree);
294   free_tree(tree);
295 }
296
297 int smpi_coll_tuned_alltoall_ompi(void *sendbuf, int sendcount,
298                                    MPI_Datatype sendtype, void *recvbuf,
299                                    int recvcount, MPI_Datatype recvtype,
300                                    MPI_Comm comm)
301 {
302   int size, sendsize;   
303   size = smpi_comm_size(comm);  
304   sendsize = smpi_datatype_size(sendtype) * sendcount;  
305   if (sendsize < 200 && size > 12) {
306     return
307         smpi_coll_tuned_alltoall_bruck(sendbuf, sendcount, sendtype,
308                                        recvbuf, recvcount, recvtype,
309                                        comm);
310   } else if (sendsize < 3000) {
311     return
312         smpi_coll_tuned_alltoall_basic_linear(sendbuf, sendcount,
313                                               sendtype, recvbuf,
314                                               recvcount, recvtype, comm);
315   } else {
316     return
317         smpi_coll_tuned_alltoall_pairwise(sendbuf, sendcount, sendtype,
318                                           recvbuf, recvcount, recvtype,
319                                           comm);
320   }
321 }
322
323 /**
324  * Alltoall Bruck
325  *
326  * Openmpi calls this routine when the message size sent to each rank < 2000 bytes and size < 12
327  * FIXME: uh, check smpi_pmpi again, but this routine is called for > 12, not
328  * less...
329  **/
330 int smpi_coll_tuned_alltoall_bruck(void *sendbuf, int sendcount,
331                                    MPI_Datatype sendtype, void *recvbuf,
332                                    int recvcount, MPI_Datatype recvtype,
333                                    MPI_Comm comm)
334 {
335   int system_tag = 777;
336   int i, rank, size, err, count;
337   MPI_Aint lb;
338   MPI_Aint sendext = 0;
339   MPI_Aint recvext = 0;
340   MPI_Request *requests;
341
342   // FIXME: check implementation
343   rank = smpi_comm_rank(comm);
344   size = smpi_comm_size(comm);
345   XBT_DEBUG("<%d> algorithm alltoall_bruck() called.", rank);
346   err = smpi_datatype_extent(sendtype, &lb, &sendext);
347   err = smpi_datatype_extent(recvtype, &lb, &recvext);
348   /* Local copy from self */
349   err =
350       smpi_datatype_copy((char *)sendbuf + rank * sendcount * sendext, 
351                          sendcount, sendtype, 
352                          (char *)recvbuf + rank * recvcount * recvext,
353                          recvcount, recvtype);
354   if (err == MPI_SUCCESS && size > 1) {
355     /* Initiate all send/recv to/from others. */
356     requests = xbt_new(MPI_Request, 2 * (size - 1));
357     count = 0;
358     /* Create all receives that will be posted first */
359     for (i = 0; i < size; ++i) {
360       if (i == rank) {
361         XBT_DEBUG("<%d> skip request creation [src = %d, recvcount = %d]",
362                rank, i, recvcount);
363         continue;
364       }
365       requests[count] =
366           smpi_irecv_init((char *)recvbuf + i * recvcount * recvext, recvcount,
367                           recvtype, i, system_tag, comm);
368       count++;
369     }
370     /* Now create all sends  */
371     for (i = 0; i < size; ++i) {
372       if (i == rank) {
373         XBT_DEBUG("<%d> skip request creation [dst = %d, sendcount = %d]",
374                rank, i, sendcount);
375         continue;
376       }
377       requests[count] =
378           smpi_isend_init((char *)sendbuf + i * sendcount * sendext, sendcount,
379                           sendtype, i, system_tag, comm);
380       count++;
381     }
382     /* Wait for them all. */
383     smpi_mpi_startall(count, requests);
384     XBT_DEBUG("<%d> wait for %d requests", rank, count);
385     smpi_mpi_waitall(count, requests, MPI_STATUS_IGNORE);
386     xbt_free(requests);
387   }
388   return MPI_SUCCESS;
389 }
390
391 /**
392  * Alltoall basic_linear (STARMPI:alltoall-simple)
393  **/
394 int smpi_coll_tuned_alltoall_basic_linear(void *sendbuf, int sendcount,
395                                           MPI_Datatype sendtype,
396                                           void *recvbuf, int recvcount,
397                                           MPI_Datatype recvtype,
398                                           MPI_Comm comm)
399 {
400   int system_tag = 888;
401   int i, rank, size, err, count;
402   MPI_Aint lb = 0, sendext = 0, recvext = 0;
403   MPI_Request *requests;
404
405   /* Initialize. */
406   rank = smpi_comm_rank(comm);
407   size = smpi_comm_size(comm);
408   XBT_DEBUG("<%d> algorithm alltoall_basic_linear() called.", rank);
409   err = smpi_datatype_extent(sendtype, &lb, &sendext);
410   err = smpi_datatype_extent(recvtype, &lb, &recvext);
411   /* simple optimization */
412   err = smpi_datatype_copy((char *)sendbuf + rank * sendcount * sendext, 
413                            sendcount, sendtype, 
414                            (char *)recvbuf + rank * recvcount * recvext, 
415                            recvcount, recvtype);
416   if (err == MPI_SUCCESS && size > 1) {
417     /* Initiate all send/recv to/from others. */
418     requests = xbt_new(MPI_Request, 2 * (size - 1));
419     /* Post all receives first -- a simple optimization */
420     count = 0;
421     for (i = (rank + 1) % size; i != rank; i = (i + 1) % size) {
422       requests[count] =
423           smpi_irecv_init((char *)recvbuf + i * recvcount * recvext, recvcount, 
424                           recvtype, i, system_tag, comm);
425       count++;
426     }
427     /* Now post all sends in reverse order
428      *   - We would like to minimize the search time through message queue
429      *     when messages actually arrive in the order in which they were posted.
430      * TODO: check the previous assertion
431      */
432     for (i = (rank + size - 1) % size; i != rank; i = (i + size - 1) % size) {
433       requests[count] =
434           smpi_isend_init((char *)sendbuf + i * sendcount * sendext, sendcount,
435                           sendtype, i, system_tag, comm);
436       count++;
437     }
438     /* Wait for them all. */
439     smpi_mpi_startall(count, requests);
440     XBT_DEBUG("<%d> wait for %d requests", rank, count);
441     smpi_mpi_waitall(count, requests, MPI_STATUS_IGNORE);
442     xbt_free(requests);
443   }
444   return err;
445 }
446
447 /**
448  * Alltoall pairwise
449  *
450  * this algorithm performs size steps (1<=s<=size) and
451  * at each step s, a process p sends iand receive to.from a unique distinct remote process
452  * size=5 : s=1:  4->0->1, 0->1->2, 1->2->3, ...
453  *          s=2:  3->0->2, 4->1->3, 0->2->4, 1->3->0 , 2->4->1
454  *          ....
455  * Openmpi calls this routine when the message size sent to each rank is greater than 3000 bytes
456  **/
457 int smpi_coll_tuned_alltoall_pairwise(void *sendbuf, int sendcount,
458                                       MPI_Datatype sendtype, void *recvbuf,
459                                       int recvcount, MPI_Datatype recvtype,
460                                       MPI_Comm comm)
461 {
462   int system_tag = 999;
463   int rank, size, step, sendto, recvfrom, sendsize, recvsize;
464
465   rank = smpi_comm_rank(comm);
466   size = smpi_comm_size(comm);
467   XBT_DEBUG("<%d> algorithm alltoall_pairwise() called.", rank);
468   sendsize = smpi_datatype_size(sendtype);
469   recvsize = smpi_datatype_size(recvtype);
470   /* Perform pairwise exchange - starting from 1 so the local copy is last */
471   for (step = 1; step < size + 1; step++) {
472     /* who do we talk to in this step? */
473     sendto = (rank + step) % size;
474     recvfrom = (rank + size - step) % size;
475     /* send and receive */
476     smpi_mpi_sendrecv(&((char *) sendbuf)[sendto * sendsize * sendcount],
477                       sendcount, sendtype, sendto, system_tag,
478                       &((char *) recvbuf)[recvfrom * recvsize * recvcount],
479                       recvcount, recvtype, recvfrom, system_tag, comm,
480                       MPI_STATUS_IGNORE);
481   }
482   return MPI_SUCCESS;
483 }
484
485 int smpi_coll_basic_alltoallv(void *sendbuf, int *sendcounts,
486                               int *senddisps, MPI_Datatype sendtype,
487                               void *recvbuf, int *recvcounts,
488                               int *recvdisps, MPI_Datatype recvtype,
489                               MPI_Comm comm)
490 {
491   int system_tag = 889;
492   int i, rank, size, err, count;
493   MPI_Aint lb = 0, sendext = 0, recvext = 0;
494   MPI_Request *requests;
495
496   /* Initialize. */
497   rank = smpi_comm_rank(comm);
498   size = smpi_comm_size(comm);
499   XBT_DEBUG("<%d> algorithm basic_alltoallv() called.", rank);
500   err = smpi_datatype_extent(sendtype, &lb, &sendext);
501   err = smpi_datatype_extent(recvtype, &lb, &recvext);
502   /* Local copy from self */
503   err =
504       smpi_datatype_copy((char *)sendbuf + senddisps[rank] * sendext, 
505                          sendcounts[rank], sendtype,
506                          (char *)recvbuf + recvdisps[rank] * recvext, 
507                          recvcounts[rank], recvtype);
508   if (err == MPI_SUCCESS && size > 1) {
509     /* Initiate all send/recv to/from others. */
510     requests = xbt_new(MPI_Request, 2 * (size - 1));
511     count = 0;
512     /* Create all receives that will be posted first */
513     for (i = 0; i < size; ++i) {
514       if (i == rank || recvcounts[i] == 0) {
515         XBT_DEBUG
516             ("<%d> skip request creation [src = %d, recvcounts[src] = %d]",
517              rank, i, recvcounts[i]);
518         continue;
519       }
520       requests[count] =
521           smpi_irecv_init((char *)recvbuf + recvdisps[i] * recvext, 
522                           recvcounts[i], recvtype, i, system_tag, comm);
523       count++;
524     }
525     /* Now create all sends  */
526     for (i = 0; i < size; ++i) {
527       if (i == rank || sendcounts[i] == 0) {
528         XBT_DEBUG
529             ("<%d> skip request creation [dst = %d, sendcounts[dst] = %d]",
530              rank, i, sendcounts[i]);
531         continue;
532       }
533       requests[count] =
534           smpi_isend_init((char *)sendbuf + senddisps[i] * sendext, 
535                           sendcounts[i], sendtype, i, system_tag, comm);
536       count++;
537     }
538     /* Wait for them all. */
539     smpi_mpi_startall(count, requests);
540     XBT_DEBUG("<%d> wait for %d requests", rank, count);
541     smpi_mpi_waitall(count, requests, MPI_STATUS_IGNORE);
542     xbt_free(requests);
543   }
544   return err;
545 }