Search code examples
cperformanceparallel-processingmpihpc

How to sum a 2D array in C using MPI


This is the program I am using to sum all values in a 1D array, and it works correctly. But how do I modify it to work on 2D array? Imagine variable a is something like a = { {1,2}, {3,4}, {5,6} };.

I tried few solutions but they are not working, so can someone explain few important changes to make to make it compatible with 2D array also.

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

// size of array
#define n 10

int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Temporary array for slave process
int a2[1000];

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

    int pid, np,
        elements_per_process,
        n_elements_recieved;
    // np -> no. of processes
    // pid -> process id

    MPI_Status status;

    // Creation of parallel processes
    MPI_Init(&argc, &argv);

    // find out process ID,
    // and how many processes were started
    MPI_Comm_rank(MPI_COMM_WORLD, &pid);
    MPI_Comm_size(MPI_COMM_WORLD, &np);

    // master process
    if (pid == 0) {

        int index, i;
        elements_per_process = n / np;

        // check if more than 1 processes are run
        if (np > 1) {
            // distributes the portion of array
            // to child processes to calculate
            // their partial sums
            for (i = 1; i < np - 1; i++) {
                index = i * elements_per_process;

                MPI_Send(&elements_per_process,
                    1, MPI_INT, i, 0,
                    MPI_COMM_WORLD);
                MPI_Send(&a[index],
                    elements_per_process,
                    MPI_INT, i, 0,
                    MPI_COMM_WORLD);
            }
            // last process adds remaining elements
            index = i * elements_per_process;
            int elements_left = n - index;

            MPI_Send(&elements_left,
                1, MPI_INT,
                i, 0,
                MPI_COMM_WORLD);
            MPI_Send(&a[index],
                elements_left,
                MPI_INT, i, 0,
                MPI_COMM_WORLD);
        }

        // master process add its own sub array
        int sum = 0;
        for (i = 0; i < elements_per_process; i++) 
            sum += a[i];

        // collects partial sums from other processes
        int tmp;
        for (i = 1; i < np; i++) {
            MPI_Recv(&tmp, 1, MPI_INT,
                MPI_ANY_SOURCE, 0,
                MPI_COMM_WORLD,
                &status);
            int sender = status.MPI_SOURCE;
            sum += tmp;
        }

        // prints the final sum of array
        printf("Sum of array is : %d\n", sum);



    }
    // slave processes
    else {
        MPI_Recv(&n_elements_recieved,
            1, MPI_INT, 0, 0,
            MPI_COMM_WORLD,
            &status);

        // stores the received array segment
        // in local array a2
        MPI_Recv(&a2, n_elements_recieved,
            MPI_INT, 0, 0,
            MPI_COMM_WORLD,
            &status);

        // calculates its partial sum
        int partial_sum = 0;
        for (int i = 0; i < n_elements_recieved; i++)
            partial_sum += a2[i];

        // sends the partial sum to the root process
        MPI_Send(&partial_sum, 1, MPI_INT,
            0, 0, MPI_COMM_WORLD);
    }

    // cleans up all MPI state before exit of process
    MPI_Finalize();




    return 0;
}

Solution

  • You can simplify a lot by using MPI_Reduce instead of MPI_Send/MPI_Recv:

    Reduces values on all processes to a single value

    A nice tutorial about that routine can be found here.

    So each process contains an array (e.g., process 0 { 1, 2, 3, 4, 5} and process 1 {6, 7, 8, 9, 10 }) and performs the partial sum of that array. In the end, each process uses MPI_Reduce to sum all the partial sums into a single value available to the master process (it could have been another process as well). Have a look at this example:

    #include <mpi.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        int np, pid;
        MPI_Init(&argc, &argv);
        MPI_Comm_rank(MPI_COMM_WORLD, &pid);
        MPI_Comm_size(MPI_COMM_WORLD, &np);
    
        int partial_sum = 0;
        if (pid == 0) {
            int a[] = { 1, 2, 3, 4, 5};
            for(int i = 0; i < 5; i++)
               partial_sum += a[i];        
        }
        else  if (pid == 1){
            int a[] = {6, 7, 8, 9, 10};
            for(int i = 0; i < 5; i++) 
               partial_sum += a[i];
        }
        int sum;
        MPI_Reduce(&partial_sum, &sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
    
        if (pid == 0){
           printf("Sum of array is : %d\n", sum);
        }
     
        MPI_Finalize();
        return 0;
    }
    

    This code only works with 2 processes (and it is kind of silly( but I am using it to showcase the use of the MPI_Reduce.

    I tried few solutions but they are not working, so can someone explain few important changes to make to make it compatible with 2D array also.

    If you adapt your code to use the MPI_Reduce as I have shown, then it does not matter if it a 1D or 2D array, because you will first do the partial sum into a single value and then performance the reduction.

    Alternatively, you can also have each row assigned to a process and then perform a reduction of the entire array, and then the master process performs the sum of the resulting array.

    An example:

    #include <mpi.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        int np, pid;
        MPI_Status status;
        MPI_Init(&argc, &argv);
        MPI_Comm_rank(MPI_COMM_WORLD, &pid);
        MPI_Comm_size(MPI_COMM_WORLD, &np);
    
        int partial_sum = 0;
        int size = 5;
        int a[5] = {1, 2, 3 , 4, 5};
        int sum[5] = {0};
        MPI_Reduce(&a, &sum, size, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
    
        if (pid == 0){
           int total_sum = 0;
           for(int i = 0; i < size; i++)
               total_sum += sum[i];
           printf("Sum of array is : %d\n", total_sum);
        }
    
        MPI_Finalize();
        return 0;
    }
    

    Output (for two processes):

    Sum of array is : 30