Search code examples
mpicommunicator

Need help understanding MPI_Comm_create


Regarding MPI_Comm_create, the MPI standard says

MPI_COMM_CREATE(comm, group, newcomm)

... The function is collective and must be called by all processes in the group of comm.

I took this to mean that, for instance, if the comm argument is MPI_COMM_WORLD, then all processes must call MPI_COMM_WORLD.

However, I wrote a variation on a code available on the internet demonstrating the use of MPI_Comm_create. It is below. You can see that there are two spots where MPI_Comm_create is called, and not by all processes. And yet the code runs just fine.

Did I get lucky? Did I stumble onto some implementation-dependent feature? Am I misunderstanding the MPI standard? Is the idea that the two calls together result in everyone calling MPI_Comm_create so "at the end of the day" it's OK? Thanks. Here's the code:

#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"


int main(int argc, char **argv) {

   MPI_Comm even_comm, odd_comm;
   MPI_Group even_group, odd_group, world_group;
   int id, even_id, odd_id;
   int *even_ranks, *odd_ranks;
   int num_ranks, num_even_ranks, num_odd_ranks;
   int err_mpi, i, j;
   int even_sum, odd_sum;


   err_mpi = MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &num_ranks);
   MPI_Comm_rank(MPI_COMM_WORLD, &id);

   MPI_Comm_group(MPI_COMM_WORLD, &world_group);

   num_even_ranks = (num_ranks+1)/2;
   even_ranks = (int *)malloc(num_even_ranks*sizeof(int));
   j=0;
   for (i=0; i<num_ranks; i++){
      if (i%2 == 0) {
         even_ranks[j] = i;
         j++;
      }
   } 

   num_odd_ranks = num_ranks/2;
   odd_ranks = (int *)malloc(num_odd_ranks*sizeof(int));
   j=0;
   for (i=0; i<num_ranks; i++){
      if (i%2 == 1) {
         odd_ranks[j] = i;
         j++;
      }
   } 

   if (id%2 == 0){
      MPI_Group_incl(world_group, num_even_ranks, even_ranks, &even_group);
      // RIGHT HERE, all procs are NOT calling!
      MPI_Comm_create(MPI_COMM_WORLD, even_group, &even_comm);
      MPI_Comm_rank(even_comm, &even_id);
      odd_id = -1;
   } else {
      MPI_Group_incl(world_group, num_odd_ranks, odd_ranks, &odd_group);
      // RIGHT HERE, all procs are NOT calling!
      MPI_Comm_create(MPI_COMM_WORLD, odd_group, &odd_comm);
      MPI_Comm_rank(odd_comm, &odd_id);
      even_id = -1;
   }

   // Just to have something to do, we'll some up the ranks of
   // the various procs in each communicator.       
   if (even_id != -1) MPI_Reduce(&id, &even_sum, 1, MPI_INT, MPI_SUM, 0, even_comm);
   if (odd_id != -1) MPI_Reduce(&id, &odd_sum, 1, MPI_INT, MPI_SUM, 0, odd_comm);

   if (odd_id == 0) printf("odd sum: %d\n", odd_sum);
   if (even_id == 0) printf("even sum: %d\n", even_sum);

   MPI_Finalize();

}

Solution

  • Although the comm_create is called from two different lines of code, the important point is that all processes in COMM_WORLD are calling comm_create at the same time. The fact that they are not from the same line of code is not relevant - in fact, the MPI library doesn't even know where comm_create is being called from.

    A simpler example would be calling Barrier from the two branches; regardless of which line is executed, all processes are executing the same barrier so the code will work as expected.

    You could easily rewrite the code to be called from the same line: simply have variables called "num_ranks", "mycomm", "mygroup" and "myid" and an array called "ranks" and set them equal to the even or odd variables depending on rank. All processes could then call:

    MPI_Group_incl(world_group, num_ranks, ranks, &mygroup);
    MPI_Comm_create(MPI_COMM_WORLD, mygroup, &mycomm);
    MPI_Comm_rank(mycomm, &myid);
    

    and if you really wanted you could reassign these back afterwards, e.g.

    if (id%2 == 0) even_comm = mycomm;