HPC MPI LAB 1 : Vector Addition
Name: Mridul Harish
Roll No: CED18I034
Programming Environment: MPI
Problem: Matrix Multiplication
Date: 21st October
Hardware Configuration:
CPU NAME : Intel Core i5-8250U @ 8x 3.4GHz
Number of Sockets : 1
Cores per Socket : 4
Threads per core : 2
L1 Cache size : 32KB
L2 Cache size : 256KB
L3 Cache size(Shared): 6MB
RAM : 8 GB
Collective Communication MPI Code
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[])
{
    int myid, np;
    double startwtime, endwtime, totalTime;
    int namelen;
    int n = 100000;
    double a[n + 50], b[n + 50], c[n + 50];
    int i, j;
    int s, s0;
    char processor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Init(&argc, &argv);
    startwtime = MPI_Wtime();
    MPI_Comm_size(MPI_COMM_WORLD, &np);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Get_processor_name(processor_name, &namelen);
    //fprintf(stderr, "Process %d on %s\n", myid, processor_name);
    fflush(stderr);
    // Vector 1 Reading
    for (i = 0; i < n; i++)
        a[i] = i;
    // Vector 2 Reading
    for (i = 0; i < n; i++)
        b[i] = i;
    if (myid == 0)
    {
        MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);
        s = (int)floor(n / np);
        s0 = n % np;
        int a_recv[s];
        int b_recv[s];
        long int c_recv[s];
        if (s0 != 0)
        {
            s = s + 1;
            for (i = 0; i < ((s * np) - n); i++)
            {
                a[n + i] = 1;
                b[n + i] = 1;
            }
        }
        MPI_Bcast(&s, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Scatter(a, s, MPI_INT, a_recv, s, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Scatter(b, s, MPI_INT, b_recv, s, MPI_INT, 0, MPI_COMM_WORLD);
        for (i = 0; i < s; i++)
        {
            c_recv[i] = a_recv[i] + b_recv[i];
        }
        MPI_Gather(c_recv, s, MPI_LONG, c, s, MPI_LONG, 0,
MPI_COMM_WORLD);
        //for (i = 0; i < n; i++)
            //printf("%d %d %d %ld\n", i, a[i], b[i], c[i]);
        endwtime = MPI_Wtime();
           totalTime = endwtime - startwtime;
           printf("%f\n", totalTime);
    }
    else
    {
           MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);
           MPI_Bcast(&s, 1, MPI_INT, 0, MPI_COMM_WORLD);
           double a_recv[s], b_recv[s], c_recv[s];
           MPI_Scatter(a, s, MPI_INT, a_recv, s, MPI_INT, 0, MPI_COMM_WORLD);
           MPI_Scatter(b, s, MPI_INT, b_recv, s, MPI_INT, 0, MPI_COMM_WORLD);
           for (i = 0; i < s; i++)
           {
               c_recv[i] = a_recv[i] + b_recv[i];
           }
           MPI_Gather(c_recv, s, MPI_LONG, c, s, MPI_LONG, 0,
MPI_COMM_WORLD);
    }
    MPI_Finalize();
}
Point to Point communication MPI Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define n 100000
double a[n], b[n];
double c[n] = {0};
int main(int argc, char *argv[])
{
    int myid, np, elements_per_process, n_elements_recieved;
    MPI_Status status;
    double startwtime, endwtime, totalTime;
    MPI_Init(&argc, &argv);
    startwtime = MPI_Wtime();
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &np);
    if (myid == 0)
    {
        for (int i = 0; i < n; i += 1)
        {
            a[i] = (i + 1);
            b[i] = (i + 1);
        }
        int idx, i;
        if(np == 1)
            elements_per_process = n;
        else
            elements_per_process = n / (np - 1);
        if (np > 1)
        {
            for (i = 1; i < np - 1; i++)
            {
                idx = (i - 1) * elements_per_process;
                MPI_Send(&elements_per_process,1, MPI_DOUBLE, i,
0,MPI_COMM_WORLD);
                MPI_Send(&a[idx],elements_per_process,MPI_DOUBLE, i,
0,MPI_COMM_WORLD);
                MPI_Send(&b[idx],elements_per_process,MPI_DOUBLE, i,
0,MPI_COMM_WORLD);
            }
            idx = (i - 1) * elements_per_process;
            int elements_left = n - idx;
            MPI_Send(&elements_left,1, MPI_DOUBLE,i, 0,MPI_COMM_WORLD);
            MPI_Send(&a[idx],elements_left,MPI_DOUBLE, i,
0,MPI_COMM_WORLD);
            MPI_Send(&b[idx],elements_left,MPI_DOUBLE, i,
0,MPI_COMM_WORLD);
        }
        for (i = 1; i < np; i++)
        {
            int n_elements_recieved;
            idx = (i - 1) * elements_per_process;
            MPI_Recv(&n_elements_recieved,1, MPI_DOUBLE, i,
0,MPI_COMM_WORLD,&status);
               MPI_Recv(&c[idx], n_elements_recieved,MPI_DOUBLE, i,
0,MPI_COMM_WORLD,&status);
               int sender = status.MPI_SOURCE;
           }
           endwtime = MPI_Wtime();
           totalTime = endwtime - startwtime;
           printf("%f\n", totalTime);
    }
    else
    {
           MPI_Recv(&n_elements_recieved,1, MPI_DOUBLE, 0,
0,MPI_COMM_WORLD,&status);
           char processor_name[MPI_MAX_PROCESSOR_NAME];
           double a_recv[n + 1000], b_recv[n + 1000], c_recv[n + 1000];
           int name_len;
           MPI_Get_processor_name(processor_name, &name_len);
           MPI_Recv(&a_recv, n_elements_recieved,MPI_DOUBLE, 0,
0,MPI_COMM_WORLD,&status);
           MPI_Recv(&b_recv, n_elements_recieved,MPI_DOUBLE, 0,
0,MPI_COMM_WORLD,&status);
           for (int i = 0; i < n_elements_recieved; i++)
               c_recv[i] = a_recv[i] + b_recv[i];
           MPI_Send(&n_elements_recieved,1, MPI_DOUBLE,0, 0,MPI_COMM_WORLD);
           MPI_Send(&c_recv, n_elements_recieved, MPI_DOUBLE,0, 0,
MPI_COMM_WORLD);
    }
    MPI_Finalize();
    return 0;
}
Compilation and Execution
Compile using mpicc to include the mpi library ;
mpicc vector_add.c -o vector_add
For execution;
mpirun -n 25 -f machinefile ./vector_add
Observations
Speed up can be found using the following formula;
      S(n)=T(1)/T(n) where, S(n) = Speedup for thread count ‘n’
                           T(1) = Execution Time for Thread count ‘1’ (serial code)
                           T(n) = Execution Time for Thread count ‘n’ (serial code)
Parallelization Fraction can be found using the following formula;
      S(n)=1/((1 - p) + p/n where, S(n) = Speedup for thread count ‘n’
                                    n = Number of threads
                                    p = Parallelization fraction
Collective Communication;
No of              Execution                                   Parallel
Threads(P)         Time(T(P))             Speedup(S(P))        Fraction(f(P))
              1                 0.00576
              2             0.002708             2.127031019        1.059722222
              4             0.002645             2.177693762       0.7210648148
              6             0.089785             0.064153255        -17.50520833
              8                 0.13245        0.04348810872        -25.13690476
             12             0.338068           0.01703799236        -62.93712121
             16             0.428783           0.01343336839        -78.33759259
             20             1.112917          0.005175588117        -202.3313231
             24             1.935339           0.00297622277         -349.561413
             32             6.079448         0.0009474544399        -1088.474552
             48             6.102137         0.0009439316095        -1080.917908
             64             3.791373          0.001519238545        -667.6566138
             128           14.708826         0.0003916016139        -2572.714961
Point to point Communication;
No of              Execution                                   Parallel
Threads(P)         Time(T(P))              Speedup(S(P))       Fraction(f(P))
              1             0.001028
              2             0.002171            0.4735145094     -2.223735409
              4                 0.00299          0.343812709     -2.544747082
              6             0.032904           0.03124240214     -37.20933852
              8             0.170045          0.006045458555     -187.9010561
             12             0.434316            0.0023669402     -459.8033251
             16             0.061818           0.01662946067     -63.07652399
             20             0.354108           0.00290306912     -361.5400369
             24                 0.111944       0.00918316301     -112.5860261
             32             0.333079          0.003086354889     -333.4263838
             48             2.674903         0.0003843130013     -2656.387118
             64             4.826712          0.000212981425     -4768.756964
             128           23.345798         0.0000440336201     -22887.73063
Inference
The performance of the program worsens with the cluster execution as the same
resources are divided among different virtual machines.
At any given instance of the program’s runtime, there are 4 OS (1 Windows 11 OS and
3 Ubuntu 20.10) running, using the common resources from the laptop. This causes a
bottleneck in the performance of the program.
Along with the OS, there are multiple background and foreground processes running,
which use the system resources.
Also, the complexity of the algorithm is low, O(n).
The communication overhead crosses the computation overhead at 128 processors,
causing the drastic reduction in the performance of the program.
At 256 processors, my computer stopped working because the computations overhead
were too much.
Collective communication offers better performance compared to Point-to-point
communication.