Search code examples
arrayscparallel-processingmpiscatter

How to scatter multiple variables in an array for MPI_Scatter


I am currently struggling to equally distribute an array with 8 integers to 2 integers per 4 processors. I used MPI_Bcast to let every processors to know there are total array of 8 and each of those will have 2 integers array called "my_input".

MPI_Bcast(&totalarray,1,MPI_INT,0,MPI_COMM_WORLD);
MPI_Bcast(&my_input,2,MPI_INT,0,MPI_COMM_WORLD);

MPI_Scatter (input, 2 , MPI_INT, &my_input, 2 , MPI_INT, 0, MPI_COMM_WORLD );
//MPI_Barrier (MPI_COMM_WORLD);
printf("\n my input is %d & %d and rank is  %d \n" , my_input[0], my_input[1] , rank);

However after scattering, I see the print function cannot print the 'rank' but all the integers from the 8 integers array. How should I program in order to equally distribute the number of arrays to other processors from root?

Here is my full code (it is just for testing a total of 8 integers, therefore scanf I will enter '8'):

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

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

//initailise MPI
    MPI_Init(&argc, &argv);

    //Variable to identify processor and total number of processors
    int rank, size;
    int my_input[0];

    //initailse total array variable
    int totalarray =0;

    //initialise memory array
    int* input;

    //range of random number
    int upper = 100, lower = 0;

    //declare processor rank
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    //declare total size of processor
    MPI_Comm_size(MPI_COMM_WORLD, &size);


    //let root gather N elements from user
    if (rank == 0)
    {
        printf("Enter a number from 1 to 1000: ");
        fflush(stdout);

        int number;

        //ask user to input number of elements
        scanf("%d",&number);
        printf("Your number is %d\n",number);

        //Fill the array to power of 2
        int totalarray = pow(2, ceil(log(number)/log(2)));

        input[totalarray];
        my_input[totalarray/size];

        //allocate memory for the array
        input = malloc(totalarray * sizeof(int) );

        //Add randomise number until N elements
        for(int i =0; i<=totalarray ; i++)
        {
            if( i<number)
            {
                input[i] = (rand() % (upper - lower + 1)) + lower; ;
            }
            //padding zero to the extra elements
            else if(number <= i < totalarray)
            {
                input[i] = 0;
            }
        }

        //confirm the input array
        printf("the input is: ");

          for(int i =0; i < totalarray ; i++)
        {
          printf(  "%d  ", input[i]);
        }

    }
    
    MPI_Bcast(&totalarray,1,MPI_INT,0,MPI_COMM_WORLD);
    MPI_Bcast(&my_input,2,MPI_INT,0,MPI_COMM_WORLD);

    MPI_Scatter (input, 2 , MPI_INT, &my_input, 2 , MPI_INT, 0, MPI_COMM_WORLD );
    //MPI_Barrier (MPI_COMM_WORLD);
    printf("\n my input is %d & %d and rank is  %d \n" , my_input[0], my_input[1] , rank);


    MPI_Finalize();

    return 0;
}

Solution

  • I used MPI_Bcast to let every processors to know there are total array of 8 and each of those will have 2 integers array called "my_input".

    Yes, that makes sense.

    However after scattering, I see the print function cannot print the 'rank' but all the integers from the 8 integers array. How should I program in order to equally distribute the number of arrays to other processors from root?

    You have some issues with your code. For instance, you declare the variables my_input, totalarray, and input as:

    int my_input[0];
    ...
    int totalarray =0;
    ...
    int* input;
    

    and then within if (rank == 0) you redefine them again:

    int totalarray = pow(2, ceil(log(number)/log(2)));
    input[totalarray];
    my_input[totalarray/size];
    input = malloc(totalarray * sizeof(int) );
    

    This is not correct, alternatively what you can do is to declare both arrays as int*, namely:

    int *my_input;
    int *input;
    

    then allocate their space as soon as you know how many elements there will be in each of those arrays.

    The input array can be allocated right after the user has inserted the size of that array:

       //ask user to input number of elements
        scanf("%d",&number);
        printf("Your number is %d\n",number);
        input = malloc(totalarray * sizeof(int));
    

    and the my_input array after the master process has broadcast the input size to the other processes:

    MPI_Bcast(&totalarray, 1, MPI_INT, 0, MPI_COMM_WORLD);
    int *my_input = malloc((totalarray/size) * sizeof(int));
    

    For the variable totalarray just do not declare again within if (rank == 0). Because if you do so, then int totalarray = pow(2, ceil(log(number)/log(2))); will be a different variable that will only exist in the scope of the if (rank == 0).

    The second MPI_Bcast call

    MPI_Bcast(&my_input,2,MPI_INT,0,MPI_COMM_WORLD);
    

    is unless, since you want to

    to equally distribute total 8 integers in an array to 2 integers for 4 processors.

    and not that every process has the entire contend of the my_input array of the master process.

    For that you need the MPI_Scatter which you do. However, instead of

      MPI_Scatter (input, 2 , MPI_INT, &my_input, 2 , MPI_INT, 0, MPI_COMM_WORLD );
    

    do not hardcode the size of the inputs, because if you want to test with different input sizes and/or with a different number of processes the code will not work, do the following instead:

      int size_per_process = totalarray/size;
      MPI_Scatter (input, size_per_process , MPI_INT, my_input, size_per_process , MPI_INT, 0, MPI_COMM_WORLD );
    

    The loop for(int i =0; i<=totalarray ; i++) should actually be for(int i =0; i< totalarray ; i++), otherwise you are getting out of boundaries of the array input. Personal opinion, but I think that the adding of the random values logic reads better this way:

        for(int i =0; i < number ; i++)
           input[i] = (rand() % (upper - lower + 1)) + lower; 
        for(int i = number; i < totalarray; i++)
           input[i] = 0;
    

    The final code would look like the following:

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include "mpi.h"
    
    int main(int argc, char *argv[])
    {
        MPI_Init(&argc, &argv);
        int rank, size;
        int *input;
        int totalarray;
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        MPI_Comm_size(MPI_COMM_WORLD, &size);
    
        if (rank == 0){
            printf("Enter a number from 1 to 1000: ");
            fflush(stdout);
    
            int number;
            scanf("%d",&number);
            printf("Your number is %d\n",number);
    
            totalarray = pow(2, ceil(log(number)/log(2)));
    
            input = malloc(totalarray * sizeof(int));
             
            int upper = 100, lower = 0;
            for(int i = 0; i < number ; i++)
               input[i] = (rand() % (upper - lower + 1)) + lower;
            for(int i = number; i < totalarray; i++)
               input[i] = 0;
    
            printf("the input is: ");
            for(int i =0; i < totalarray ; i++)
               printf(  "%d  ", input[i]);
        }
        
        MPI_Bcast(&totalarray, 1, MPI_INT, 0, MPI_COMM_WORLD);
        int size_per_process = totalarray / size;
        int *my_input = malloc(size_per_process * sizeof(int));
        printf("SIZE PER %d\n", size_per_process);
        MPI_Scatter (input, size_per_process, MPI_INT, my_input, size_per_process, MPI_INT, 0, MPI_COMM_WORLD );
        printf("\n my input is %d & %d and rank is  %d \n" , my_input[0], my_input[1] , rank);
    
    
        MPI_Finalize();
        return 0;
    }
    

    The last print can also be made to be more generic by printing the entire my_input rather than just the first two positions.