Search code examples
cparallel-processingmpiopenmpi

How to calculate percentage ratio?


I am new to mpi and I am trying to write a mini C program that calculates the percentage ratio of numbers that the user inputs.

The percentage ratio is calculated by that expression

`δi = ((xi – xmin ) / (xmax – xmin )) * 100`. 

The numbers that the user inputs are stored in an array of fixed size data[100] and are scattered to all processes (this program is supposed to work only with four processes). The problem I am facing is that the division doesn't work although all the processes have the data. For example if the user inputs the numbers {1, 2, 3, 4} the expected percentage ratio according to the mathematical expression is {0, 33.3, 66.6, 100} but instead I am getting {0,0,100,100}. This is what I have.

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


int main(int argc, char** argv){
    int my_rank;
    int total_processes;
    int root = 0;
    int data[100];
    int loc_data[100];

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &total_processes);

    int input_size = 0;
    if (my_rank == 0){
        printf("Input how many numbers: ");
        scanf("%d", &input_size);
        
        printf("Input the elements of the array: ");
        for(int i=0; i<input_size; i++){
            scanf("%d", &data[i]);
        }
    }

    MPI_Bcast(&input_size, 1, MPI_INT, root, MPI_COMM_WORLD);

    int loc_num = input_size/total_processes;

    MPI_Scatter(&data, loc_num, MPI_INT, loc_data, loc_num, MPI_INT, root, MPI_COMM_WORLD);

    int global_max = 0;
    int global_min = 0;
    MPI_Reduce(&loc_data, &global_max, 1, MPI_INT, MPI_MAX, root, MPI_COMM_WORLD);
    MPI_Reduce(&loc_data, &global_min, 1, MPI_INT, MPI_MIN, root, MPI_COMM_WORLD);
    

    float loc_delta[100];
    int x = 0;
    int y = 0;
    float p = 0;

     for(int j = 0; j< loc_num; j++){
        x = loc_data[j] - global_min;
        y = global_max - global_min;
    }

    MPI_Bcast(&y, 1, MPI_INT, root, MPI_COMM_WORLD); 
    
    for(int j = 0; j< loc_num ; j++){
        p = (x / y) * 100;
        printf("p= %f \n", p);
        loc_delta[j] = p; 
    }

    float final_delta[100];
    MPI_Gather(&loc_delta, 1, MPI_FLOAT, final_delta, 1, MPI_FLOAT, root, MPI_COMM_WORLD);


    if(my_rank == 0){
        printf("max number: %d\n", global_max);
        printf("min number: %d\n", global_min);
        
        for(int i = 0; i<input_size; i++)
            printf("delta[%d]: %.2f | ", i+1, final_delta[i]);
    }

    printf("\n");

        MPI_Finalize();

    return 0;
}

Solution

  • There are several issues with your code.

    First:

    int global_max = 0;
    int global_min = 0;
    MPI_Reduce(&loc_data, &global_max, 1, MPI_INT, MPI_MAX, root, MPI_COMM_WORLD);
    MPI_Reduce(&loc_data, &global_min, 1, MPI_INT, MPI_MIN, root, MPI_COMM_WORLD);
    

    unfortunately,

    MPI does not get the minimum of all elements in the array, you have to do that manually. (source)

    Therefore, one needs to first calculate the min and the max within each process' array, and then one can reduce those min and max results among the other processes. Since, all processes should have the min and max of that array, instead of MPI_Reduce, you should use MPI_Allreduce. And your code would look like the following:

    int local_max = loc_data[0];
    int local_min = loc_data[0];
    for(int i = 1; i < loc_num; i++){
       local_max = (local_max > loc_data[i]) ?  local_max : loc_data[i];
       local_min = (local_min < loc_data[i]) ?  local_min : loc_data[i];  
    }
    
    int global_max = local_max;
    int global_min = local_min;
    
    MPI_Allreduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
    MPI_Allreduce(&local_min, &global_min, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
    

    Unless you are assuming that loc_num=1, which you should not, this code

      for(int j = 0; j< loc_num; j++){
            x = loc_data[j] - global_min;
            y = global_max - global_min;
        }
    

    overrides the same x and y. Moreover, you should not call MPI_Bcast(&y, 1, MPI_INT, root, MPI_COMM_WORLD);, you want for all the processes to first calculate in parallel their work based on the formula:

    δi = ((xi – xmin ) / (xmax – xmin )) * 100.
    

    and only then send their work back to the master process. So each process should apply that formula to their input indices, stored the results in an array and send it back to the master process. Like so:

    float loc_delta[100];
    float y = global_max - global_min;
    for(int j = 0; j< loc_num; j++){
        loc_delta[j] = (((float) (loc_data[j] - global_min) / y) * 100.0); 
    }
     
    float final_delta[100];
    MPI_Gather(&loc_delta, loc_num, MPI_FLOAT, final_delta, loc_num, MPI_FLOAT, root, MPI_COMM_WORLD);
    

    Notice that I am casting (((float) (loc_data[j] - global_min) / y) * 100.0); to float. Otherwise, C would return an int representation of the result.