MPI Coding Practice

  • Compiling a program for MPI is almost just like compiling a regular C or C++ program

    • The C compiler is mpicc and the C compiler is mpic.

    • For example, to compile MyProg.c you would use a command like

    • mpicc - O2 -o MyProg MyProg.c

1. List of programms

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

/**
 * @brief Illustrates how to use an allgather.
 * @details This application is meant to be run with 3 MPI processes. Every MPI
 * process begins with a value, then every MPI process collects the entirety of
 * the data gathered and prints them. It can be visualised as follows:
 *
 * +-----------+  +-----------+  +-----------+
 * | Process 0 |  | Process 1 |  | Process 2 |
 * +-+-------+-+  +-+-------+-+  +-+-------+-+
 *   | Value |      | Value |      | Value |
 *   |   0   |      |  100  |      |  200  |
 *   +-------+      +-------+      +-------+
 *       |________      |      ________|
 *                |     |     | 
 *             +-----+-----+-----+
 *             |  0  | 100 | 200 |
 *             +-----+-----+-----+
 *             |   Each process  |
 *             +-----------------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 3 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Define my value
    int my_value = my_rank * 100;
    printf("Process %d, my value = %d.\n", my_rank, my_value);

    int buffer[3];
    MPI_Allgather(&my_value, 1, MPI_INT, buffer, 1, MPI_INT, MPI_COMM_WORLD);
    printf("Values collected on process %d: %d, %d, %d.\n", my_rank, buffer[0], buffer[1], buffer[2]);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
Allreduce
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use an all-reduce.
 * @details This application consists of a sum all-reduction; every MPI process
 * sends its rank for reduction before the sum of these ranks is stored in the
 * receive buffer of each MPI process. It can be visualised as follows:
 *
 * +-----------+ +-----------+ +-----------+ +-----------+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+
 *   | Value |     | Value |     | Value |     | Value |
 *   |   0   |     |   1   |     |   2   |     |   3   |
 *   +-------+     +----+--+     +--+----+     +-------+
 *            \         |           |         /
 *             \        |           |        /
 *              \       |           |       /
 *               \      |           |      /
 *                +-----+-----+-----+-----+
 *                            |
 *                        +---+---+
 *                        |  SUM  |
 *                        +---+---+
 *                        |   6   |
 *                        +-------+
 *                            |
 *                +-----+-----+-----+-----+
 *               /      |           |      \
 *              /       |           |       \
 *             /        |           |        \
 *            /         |           |         \
 *   +-------+     +----+--+     +--+----+     +-------+  
 *   |   6   |     |   6   |     |   6   |     |   6   |  
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-----------+ +-----------+ +-----------+ +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the size of the communicator
    int size = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 4)
    {
        printf("This application is meant to be run with 4 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Each MPI process sends its rank to reduction, root MPI process collects the result
    int reduction_result = 0;
    MPI_Allreduce(&my_rank, &reduction_result, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);

    printf("[MPI Process %d] The sum of all ranks is %d.\n", my_rank, reduction_result);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Alltoall
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use an all to all.
 * @details This application is meant to be run with 3 MPI processes. Every MPI
 * process begins with a buffer containing 3 integers, one for each process
 * including themselves. They also have a buffer in which receive the integer
 * that has been sent by each other process for them. It can be visualised as
 * follows:
 *
 * +-----------------------+ +-----------------------+ +-----------------------+
 * |       Process 0       | |       Process 1       | |       Process 2       |
 * +-------+-------+-------+ +-------+-------+-------+ +-------+-------+-------+
 * | Value | Value | Value | | Value | Value | Value | | Value | Value | Value |
 * |   0   |  100  |  200  | |  300  |  400  |  500  | |  600  |  700  |  800  |
 * +-------+-------+-------+ +-------+-------+-------+ +-------+-------+-------+
 *     |       |       |_________|_______|_______|_________|___    |       |
 *     |       |    _____________|_______|_______|_________|   |   |       |
 *     |       |___|_____________|_      |      _|_____________|___|       |
 *     |      _____|_____________| |     |     | |_____________|_____      |
 *     |     |     |               |     |     |               |     |     |
 *  +-----+-----+-----+         +-----+-----+-----+         +-----+-----+-----+
 *  |  0  | 300 | 600 |         | 100 | 400 | 700 |         | 200 | 500 | 800 |
 *  +-----+-----+-----+         +-----+-----+-----+         +-----+-----+-----+
 *  |    Process 0    |         |    Process 1    |         |    Process 2    |
 *  +-----------------+         +-----------------+         +-----------------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 3 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Define my value
    int my_values[3];
    for(int i = 0; i < 3; i++)
    {
        my_values[i] = my_rank * 300 + i * 100;
    }
    printf("Process %d, my values = %d, %d, %d.\n", my_rank, my_values[0], my_values[1], my_values[2]);

    int buffer_recv[3];
    MPI_Alltoall(&my_values, 1, MPI_INT, buffer_recv, 1, MPI_INT, MPI_COMM_WORLD);
    printf("Values collected on process %d: %d, %d, %d.\n", my_rank, buffer_recv[0], buffer_recv[1], buffer_recv[2]);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Barrier
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use an MPI barrier.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    printf("[MPI process %d] I start waiting on the barrier.\n", my_rank);
    MPI_Barrier(MPI_COMM_WORLD);
    printf("[MPI process %d] I know all MPI processes have waited on the barrier.\n", my_rank);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Bcast
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to broadcast a message.
 * @details This code picks a process as the broadcast root, and makes it
 * broadcast a specific value. Other processes participate to the broadcast as
 * receivers. These processes then print the value they received via the 
 * broadcast.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get my rank in the communicator
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Determine the rank of the broadcast emitter process
    int broadcast_root = 0;

    int buffer;
    if(my_rank == broadcast_root)
    {
        buffer = 12345;
        printf("[MPI process %d] I am the broadcast root, and send value %d.\n", my_rank, buffer);
    }
    MPI_Bcast(&buffer, 1, MPI_INT, broadcast_root, MPI_COMM_WORLD);
    if(my_rank != broadcast_root)
    {
        printf("[MPI process %d] I am a broadcast receiver, and obtained value %d.\n", my_rank, buffer);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Bsend
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to send a message in a blocking asynchronous fashion.
 * @details This application is meant to be used with 2 processes; 1 sender and
 * 1 receiver. The sender will declare a buffer containing enough space for 1
 * message that will contain 1 integer. It then attaches the buffer to MPI and
 * issues the MPI_Bsend. Finally, it detaches the buffer and frees it, while the
 * receiver prints the message received.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the number of processes and check only 2 are used.
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 2)
    {
        printf("This application is meant to be run with 2 processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank and do the corresponding job
    enum role_ranks { SENDER, RECEIVER };
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    switch(my_rank)
    {
        case SENDER:
        {
            // Declare the buffer and attach it
            int buffer_attached_size = MPI_BSEND_OVERHEAD + sizeof(int);
            char* buffer_attached = (char*)malloc(buffer_attached_size);
            MPI_Buffer_attach(buffer_attached, buffer_attached_size);

            // Issue the MPI_Bsend
            int buffer_sent = 12345;
            printf("[MPI process %d] I send value %d.\n", my_rank, buffer_sent);
            MPI_Bsend(&buffer_sent, 1, MPI_INT, RECEIVER, 0, MPI_COMM_WORLD);

            // Detach the buffer. It blocks until all messages stored are sent.
            MPI_Buffer_detach(&buffer_attached, &buffer_attached_size);
            free(buffer_attached);
            break;
        }
        case RECEIVER:
        {
            // Receive the message and print it.
            int received;
            MPI_Recv(&received, 1, MPI_INT, SENDER, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            printf("[MPI process %d] I received value: %d.\n", my_rank, received);
            break;
        }
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Comm_spawn
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to spawn MPI processes.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    MPI_Comm parent;
    MPI_Comm_get_parent(&parent);
    if(parent == MPI_COMM_NULL)
    {
        // We have no parent communicator so we have been spawned directly by the user
        MPI_Comm child;
        int spawn_error;
        printf("We are processes spawned directly by you, we now spawn a new instance of an MPI application.\n");
        MPI_Comm_spawn(argv[0], MPI_ARGV_NULL, 1, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &child, &spawn_error);
    }
    else
    {
        // We have been spawned by another MPI process
        printf("I have been spawned by MPI processes.\n");
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Comm_split
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates
 * @details MPI processes split into two groups depending on whether their rank
 * is even.
 *
 * +----------------+---+---+---+---+
 * | MPI processes  | 0 | 1 | 2 | 3 |
 * +----------------+---+---+---+---+
 * | MPI_COMM_WORLD | X | X | X | X |
 * | Subgroup A     | X |   | X |   |
 * | Subgroup B     |   | X |   | X |
 * +----------------+---+---+---+---+
 *
 * In subcommunicator A, MPI processes are assigned ranks in the same order as
 * their rank in the global communicator.
 * In subcommunicator B, MPI processes are assigned ranks in the opposite order
 * as their rank in the global communicator.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Check that 4 MPI processes are used
    int comm_size;
    MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
    if(comm_size != 4)
    {
        printf("This application is meant to be run with 4 MPI processes, not %d.\n", comm_size);
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank in the global communicator
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Determine the colour and key based on whether my rank is even.
    char subcommunicator;
    int colour;
    int key;
    if(my_rank % 2 == 0)
    {
        subcommunicator = 'A';
        colour = 0;
        key = my_rank;
    }
    else
    {
        subcommunicator = 'B';
        colour = 1;
        key = comm_size - my_rank;
    }

    // Split de global communicator
    MPI_Comm new_comm;
    MPI_Comm_split(MPI_COMM_WORLD, colour, key, &new_comm);

    // Get my rank in the new communicator
    int my_new_comm_rank;
    MPI_Comm_rank(new_comm, &my_new_comm_rank);

    // Print my new rank and new communicator
    printf("[MPI process %d] I am now MPI process %d in subcommunicator %c.\n", my_rank, my_new_comm_rank, subcommunicator);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Exscan
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrate how to use an MPI_Exscan.
 * @details This program uses MPI_Exscan to compute a progressive sum of ranks. It
 * can be visualised as follows:
 *
 * +---------------+   +---------------+   +---------------+   +---------------+
 * | MPI process 0 |   | MPI process 1 |   | MPI process 2 |   | MPI process 3 |
 * +---------------+   +---------------+   +---------------+   +---------------+
 * |       0       |   |       1       |   |       2       |   |       3       |
 * +-------+-------+   +-------+-------+   +-------+-------+   +-------+-------+
 *         |                   |                   |                    
 *         |                +--+--+                |                    
 *         +----------------| SUM |                |                    
 *         |                +--+--+                |                    
 *         |                   |                +--+--+                 
 *         |                   +----------------| SUM |                 
 *         |                   |                +--+--+                 
 *         |                   |                   |                    
 *         |                   |                   |                    
 *          \                   \                   \                      
 *           \                   \                   \                  
 *            \                   \                   \                 
 *             \_______________    \_______________    \_______________ 
 *                             |                   |                   |
 *                             |                   |                   |
 * +-------+-------+   +-------+-------+   +-------+-------+   +-------+-------+
 * |   undefined   |   |       0       |   |       1       |   |       3       |
 * +---------------+   +---------------+   +---------------+   +---------------+
 * | MPI process 0 |   | MPI process 1 |   | MPI process 2 |   | MPI process 3 |
 * +---------------+   +---------------+   +---------------+   +---------------+
 *                                       
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Get the sum of all ranks up to the one before mine and print it
    int total;
    MPI_Exscan(&my_rank, &total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);

    // The result on MPI process 0 is undefined, do not print it
    if(my_rank == 0)
    {
        printf("[MPI process 0] Total = undefined.\n");
    }
    else
    {
        printf("[MPI process %d] Total = %d.\n", my_rank, total);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_File
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates the use of the MPI_File handle.
 * @details This applications consists in opening a file in read-write mode, or
 * create it if it does not already exist, and get a handle on that file.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    MPI_File handle;
    int access_mode = MPI_MODE_CREATE /* Create the file if it does not exist */
                    | MPI_MODE_RDWR; /* With read-write access */
    if(MPI_File_open(MPI_COMM_WORLD, "file.tmp", access_mode, MPI_INFO_NULL, &handle) != MPI_SUCCESS)
    {
        printf("[MPI process %d] Failure in opening the file.\n", my_rank);
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    printf("[MPI process %d] File opened successfully.\n", my_rank);

    if(MPI_File_close(&handle) != MPI_SUCCESS)
    {
        printf("[MPI process %d] Failure in closing the file.\n", my_rank);
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    printf("[MPI process %d] File closed successfully.\n", my_rank);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Gather
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a gather.
 * @details This application is meant to be run with 4 MPI processes. Every MPI
 * process begins with a value, then MPI process 0 is picked to gather all these
 * values and print them. It can be visualised as follows:
 *
 * +-----------+ +-----------+ +-----------+ +-----------+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+ 
 *   | Value |     | Value |     | Value |     | Value |   
 *   |   0   |     |  100  |     |  200  |     |  300  |   
 *   +-------+     +-------+     +-------+     +-------+   
 *            \            |     |            /
 *             \           |     |           /
 *              \          |     |          /
 *               \         |     |         /
 *                \        |     |        /
 *                 \       |     |       /
 *                +-----+-----+-----+-----+
 *                |  0  | 100 | 200 | 300 |
 *                +-----+-----+-----+-----+
 *                |       Process 0       |
 *                +-----------------------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 4 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 4)
    {
        printf("This application is meant to be run with 4 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Determine root's rank
    int root_rank = 0;

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Define my value
    int my_value = my_rank * 100;
    printf("Process %d, my value = %d.\n", my_rank, my_value);

    if(my_rank == root_rank)
    {
        int buffer[4];
        MPI_Gather(&my_value, 1, MPI_INT, buffer, 1, MPI_INT, root_rank, MPI_COMM_WORLD);
        printf("Values collected on process %d: %d, %d, %d, %d.\n", my_rank, buffer[0], buffer[1], buffer[2], buffer[3]);
    }
    else
    {
        MPI_Gather(&my_value, 1, MPI_INT, NULL, 0, MPI_INT, root_rank, MPI_COMM_WORLD);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Graph_get
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <mpi.h>

/**
 * @brief Illustrates how to get the indexes and edges of a graph.
 * @details This application consists of 3 MPI processes that form a fully
 * connected graph that can be visualised as follows:
 *
 * +-----+              +-----+
 * |     |------------->|     |
 * |  0  |              |  1  |
 * |     |<-------------|     |
 * +-----+              +-----+
 *  ^   |                |   ^
 *  |   |                |   |
 *  |   |    +-----+     |   |
 *  |   +--->|     |<----+   |
 *  |        |  2  |         |
 *  +--------|     |---------+
 *           +-----+
 *
 * After creating the graph, each MPI process retrieves the graph indexes and
 * edges, and prints them.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Size of the default communicator
    int comm_size;
    MPI_Comm_size(MPI_COMM_WORLD, &comm_size);

    if(comm_size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes, not %d.\n", comm_size);
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // My rank in the default communicator
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Declare the total number of neighbours until each MPI process (= the ones before + its own)
    int indexes[3] = {2, 4, 6};

    // Declare the endpoint of each edge
    int edges[6] = {1, 2, 0, 2, 0, 1};

    // Allow MPI to reorder ranks if it deems it necessary
    int reorder = true;

    // Create a communicator given the graph topology.
    MPI_Comm new_communicator;
    MPI_Graph_create(MPI_COMM_WORLD, comm_size, indexes, edges, reorder, &new_communicator);

    // Get the graph dimensions
    int number_of_indexes_retrieved;
    int number_of_edges_retrieved;
    MPI_Graphdims_get(new_communicator, &number_of_indexes_retrieved, &number_of_edges_retrieved);

    // Retrieve the indexes and edges
    int* indexes_retrieved = (int*) malloc(sizeof(int) * number_of_indexes_retrieved);
    int* edges_retrieved = (int*) malloc(sizeof(int) * number_of_edges_retrieved);
    MPI_Graph_get(new_communicator, number_of_indexes_retrieved, number_of_edges_retrieved, indexes_retrieved, edges_retrieved);

    // Print all information retrieved
    printf("[MPI process %d] %d indexes retrieved: {", my_rank, number_of_indexes_retrieved);
    for(int i = 0; i < number_of_indexes_retrieved; i++)
    {
        printf("%d", indexes_retrieved[i]);
        if(i < number_of_indexes_retrieved - 1)
        {
            printf(", ");
        }
    }
    printf("}, and %d edges retrieved: {", number_of_edges_retrieved);
    for(int i = 0; i < number_of_edges_retrieved; i++)
    {
        printf("%d", edges_retrieved[i]);
        if(i < number_of_edges_retrieved - 1)
        {
            printf(", ");
        }
    }
    printf("}.\n");

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_Graphdims_get
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <mpi.h>

/**
 * @brief Illustrates how to get the dimensions of a graph.
 * @details This application consists of 3 MPI processes that form a fully
 * connected graph that can be visualised as follows:
 *
 * +-----+              +-----+
 * |     |------------->|     |
 * |  0  |              |  1  |
 * |     |<-------------|     |
 * +-----+              +-----+
 *  ^   |                |   ^
 *  |   |                |   |
 *  |   |    +-----+     |   |
 *  |   +--->|     |<----+   |
 *  |        |  2  |         |
 *  +--------|     |---------+
 *           +-----+
 *
 * After creating the graph, each MPI process retrieves the graph dimensions and
 * prints them.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Size of the default communicator
    int comm_size;
    MPI_Comm_size(MPI_COMM_WORLD, &comm_size);

    if(comm_size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes, not %d.\n", comm_size);
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // My rank in the default communicator
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Declare the total number of neighbours until each MPI process (= the ones before + its own)
    int indexes[3] = {2, 4, 6};

    // Declare the endpoint of each edge
    int edges[6] = {1, 2, 0, 2, 0, 1};

    // Allow MPI to reorder ranks if it deems it necessary
    int reorder = true;

    // Create a communicator given the graph topology.
    MPI_Comm new_communicator;
    MPI_Graph_create(MPI_COMM_WORLD, comm_size, indexes, edges, reorder, &new_communicator);

    // Get the graph dimensions and print them
    int number_of_indexes_retrieved;
    int number_of_edge_retrieved;
    MPI_Graphdims_get(new_communicator, &number_of_indexes_retrieved, &number_of_edge_retrieved);
    printf("[MPI process %d] The graph communicator created contains %d indexes and %d edges.\n", my_rank, number_of_indexes_retrieved, number_of_edge_retrieved);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_allgather
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a gather in a non-blocking way.
 * @details This application is meant to be run with 3 MPI processes. Every MPI
 * process begins with a value, then every MPI process collects the entirety of
 * the data gathered and moves on immediately to do something else while the
 * gather progresses. They then wait for the gather to complete before printing
 * the data gathered. It can be visualised as follows:
 *
 * +-----------+  +-----------+  +-----------+
 * | Process 0 |  | Process 1 |  | Process 2 |
 * +-+-------+-+  +-+-------+-+  +-+-------+-+
 *   | Value |      | Value |      | Value |
 *   |   0   |      |  100  |      |  200  |
 *   +-------+      +-------+      +-------+
 *       |________      |      ________|
 *                |     |     |
 *             +-----+-----+-----+
 *             |  0  | 100 | 200 |
 *             +-----+-----+-----+
 *             |   Each process  |
 *             +-----------------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 3 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Define my value
    int my_value = my_rank * 100;
    printf("Process %d, my value = %d.\n", my_rank, my_value);

    // Issue the gather and move on immediately after, before the MPI_Iallgather completes
    int buffer[3];
    MPI_Request request;
    MPI_Iallgather(&my_value, 1, MPI_INT, buffer, 1, MPI_INT, MPI_COMM_WORLD, &request);

    // Do another job while the gather progresses
    // ...

    // Wait for the gather to complete before printing the values received
    MPI_Wait(&request, MPI_STATUS_IGNORE);
    printf("Values collected on process %d: %d, %d, %d.\n", my_rank, buffer[0], buffer[1], buffer[2]);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_allreduce
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking all-reduce.
 * @details This application consists of a sum all-reduction; every MPI process
 * sends its rank for reduction before the sum of these ranks is stored in the
 * receive buffer of each MPI process. It can be visualised as follows:
 *
 * +-----------+ +-----------+ +-----------+ +-----------+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+
 *   | Value |     | Value |     | Value |     | Value |
 *   |   0   |     |   1   |     |   2   |     |   3   |
 *   +-------+     +----+--+     +--+----+     +-------+
 *            \         |           |         /
 *             \        |           |        /
 *              \       |           |       /
 *               \      |           |      /
 *                +-----+-----+-----+-----+
 *                            |
 *                        +---+---+
 *                        |  SUM  |
 *                        +---+---+
 *                        |   6   |
 *                        +-------+
 *                            |
 *                +-----+-----+-----+-----+
 *               /      |           |      \
 *              /       |           |       \
 *             /        |           |        \
 *            /         |           |         \
 *   +-------+     +----+--+     +--+----+     +-------+  
 *   |   6   |     |   6   |     |   6   |     |   6   |  
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-----------+ +-----------+ +-----------+ +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the size of the communicator
    int size = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 4)
    {
        printf("This application is meant to be run with 4 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Each MPI process sends its rank to reduction, root MPI process collects the result
    int reduction_result = 0;
    MPI_Request request;
    MPI_Iallreduce(&my_rank, &reduction_result, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

    // Do some other job
    printf("Process %d issued the MPI_Iallreduce and has moved on, printing this message.\n", my_rank);

    // Wait for the MPI_Iallreduce to complete
    MPI_Wait(&request, MPI_STATUS_IGNORE);

    printf("[MPI Process %d] The sum of all ranks is %d.\n", my_rank, reduction_result);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_alltoall
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking all to all.
 * @details This application is meant to be run with 3 MPI processes. Every MPI
 * process begins with a buffer containing 3 integers, one for each process
 * including themselves. They also have a buffer in which receive the integer
 * that has been sent by each other process for them. It can be visualised as
 * follows:
 *
 * +-----------------------+ +-----------------------+ +-----------------------+
 * |       Process 0       | |       Process 1       | |       Process 2       |
 * +-------+-------+-------+ +-------+-------+-------+ +-------+-------+-------+
 * | Value | Value | Value | | Value | Value | Value | | Value | Value | Value |
 * |   0   |  100  |  200  | |  300  |  400  |  500  | |  600  |  700  |  800  |
 * +-------+-------+-------+ +-------+-------+-------+ +-------+-------+-------+
 *     |       |       |_________|_______|_______|_________|___    |       |
 *     |       |    _____________|_______|_______|_________|   |   |       |
 *     |       |___|_____________|_      |      _|_____________|___|       |
 *     |      _____|_____________| |     |     | |_____________|_____      |
 *     |     |     |               |     |     |               |     |     |
 *  +-----+-----+-----+         +-----+-----+-----+         +-----+-----+-----+
 *  |  0  | 300 | 600 |         | 100 | 400 | 700 |         | 200 | 500 | 800 |
 *  +-----+-----+-----+         +-----+-----+-----+         +-----+-----+-----+
 *  |    Process 0    |         |    Process 1    |         |    Process 2    |
 *  +-----------------+         +-----------------+         +-----------------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 3 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Define my value
    int my_values[3];
    for(int i = 0; i < 3; i++)
    {
        my_values[i] = my_rank * 300 + i * 100;
    }
    printf("Process %d, my values = %d, %d, %d.\n", my_rank, my_values[0], my_values[1], my_values[2]);

    int buffer_recv[3];
    MPI_Request request;
    MPI_Ialltoall(&my_values, 1, MPI_INT, buffer_recv, 1, MPI_INT, MPI_COMM_WORLD, &request);

    // Do another job while the non-blocking all to all progresses
    printf("[Process %d] The non-blocking all to all is in progress.\n", my_rank);

    MPI_Wait(&request, MPI_STATUS_IGNORE);
    printf("Values collected on process %d: %d, %d, %d.\n", my_rank, buffer_recv[0], buffer_recv[1], buffer_recv[2]);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_barrier
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking barrier.
 * @details This application provides a basic example about a non-blocking
 * barrier. Although this example may not be very realistic, it does illustrate
 * how to manipulate a non-blocking barrier.
 *
 * This application consists of three jobs for each process:
 * 1) Get my rank
 * 2) When I have my rank, I print it
 * 3) Once all processes obtained their rank, I say it
 *
 * Rather than using a classic barrier after 2), we can issue a non-blocking
 * barrier after 1) so that we overlap the synchronisation of the barrier with
 * the task 2). Then, once 2) is complete, we can block until the non-blocking
 * barrier completes to progress to 3).
 *
 * The difference can be visualised as follows:
 *
 *               +--------------+
 *               | Barrier sync |
 * +------+------+--------------+------+
 * | Task | Task |              | Task |
 * |   1  |   2  |              |   3  |
 * +------+------+--------------+------+
 *                                     |
 *        +--------------+             |
 *        | Barrier sync |             |
 * +------+------+-------+------+      |
 * | Task | Task |       | Task |      |
 * |  1   |   2  |       |   3  |      |
 * +------+------+--------------+      |
 *                              |      |
 *                              V      V
 * ---------------- TIME -------+------+----->
 *                               <---->
 *                                gain
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get my rank, this is task 1
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Request request;
    MPI_Ibarrier(MPI_COMM_WORLD, &request);

    // Task 2
    printf("[MPI process %d] I got my rank, it is %d, I now call MPI_Ibarrier.\n", my_rank, my_rank);

    // Task 3
    MPI_Wait(&request, MPI_STATUS_IGNORE);
    printf("[MPI process %d] The MPI_Ibarrier is complete; all processes got their rank.\n", my_rank);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_bcast
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to broadcast a message in a non-blocking way.
 * @details This code picks a process as the broadcast root, and makes it
 * broadcast a specific value. Other processes participate to the broadcast as
 * receivers. These processes then print the value they received via the 
 * broadcast.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get my rank in the communicator
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Determine the rank of the broadcast emitter process
    int broadcast_root = 0;

    int buffer;
    if(my_rank == broadcast_root)
    {
        buffer = 12345;
        printf("[MPI process %d] I am the broadcast root, and send value %d.\n", my_rank, buffer);
    }
    MPI_Request request;
    MPI_Ibcast(&buffer, 1, MPI_INT, broadcast_root, MPI_COMM_WORLD, &request);

    // Do some computation ...

    MPI_Wait(&request, MPI_STATUS_IGNORE);

    if(my_rank != broadcast_root)
    {
        printf("[MPI process %d] I am a broadcast receiver, and obtained value %d.\n", my_rank, buffer);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_reduce
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking reduce.
 * @details This application consists of a sum reduction; every MPI process
 * sends its rank for reduction before the sum of these ranks is stored in the
 * root MPI process. It can be visualised as follows, with MPI process 0 as
 * root:
 *
 * +-----------+ +-----------+ +-----------+ +-----------+
 * | Process 0 | | Process 1 | | Process 2 | | Process 3 |
 * +-+-------+-+ +-+-------+-+ +-+-------+-+ +-+-------+-+
 *   | Value |     | Value |     | Value |     | Value |
 *   |   0   |     |   1   |     |   2   |     |   3   |
 *   +-------+     +-------+     +-------+     +-------+
 *            \         |           |         /
 *             \        |           |        /
 *              \       |           |       /
 *               \      |           |      /
 *                +-----+-----+-----+-----+
 *                            |
 *                        +---+---+
 *                        |  SUM  |
 *                        +---+---+
 *                            |
 *                        +---+---+
 *                        |   6   |
 *                      +-+-------+-+
 *                      | Process 0 |
 *                      +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Determine root's rank
    int root_rank = 0;

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Each MPI process sends its rank to reduction, root MPI process collects the result
    int reduction_result = 0;
    MPI_Request request;
    MPI_Ireduce(&my_rank, &reduction_result, 1, MPI_INT, MPI_SUM, root_rank, MPI_COMM_WORLD, &request);

    // Do some other job
    printf("Process %d issued the MPI_Ireduce and has moved on, printing this message.\n", my_rank);

    // Wait for the MPI_Ireduce to complete
    MPI_Wait(&request, MPI_STATUS_IGNORE);

    if(my_rank == root_rank)
    {
        printf("The sum of all ranks is %d.\n", reduction_result);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_reduce_scatter
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking reduce scatter block.
 * @details This application is meant to be run with 3 MPI processes. It
 * consists of a sum reduction; every MPI process has four values to send for
 * reduction. The first values from the MPI process will be reduced and stored 
 * on the MPI process 0. The second and third values will be reduced separately 
 * and stored on MPI process 1, similarly with the fourth values on MPI process
 * 2. It can be visualised as follows:
 *
 *      +---------------+  +---------------+  +---------------+
 *      |   Process 0   |  |   Process 1   |  |   Process 2   |
 *      +---------------+  +---------------+  +---------------+
 *      |     Values    |  |     Values    |  |     Values    |
 *      +---+---+---+---+  +---+---+---+---+  +---+---+---+---+
 *      | 0 | 1 | 2 | 3 |  | 4 | 5 | 6 | 7 |  | 8 | 9 | 10| 11|
 *      +---+---+---+---+  +---+---+---+---+  +---+---+---+---+
 *        |    \   \   \     /  |     |  \      /   /   /   |
 *        | ____\___\___\___/___|_____|___\____/   /   /    |
 *        |/     \   \   \      |     |    \      /   /     |
 *        |       \___\___\____ | ____|_____\____/   /      |
 *        |            \   \   \|/    |      \      /       |
 *        |             \___\___|____ | ______\____/        |
 *        |                  \  |    \|/       \            |
 *        |                   \_|_____|_________\__________ |
 *        |                     |     |                    \|
 *        |                     |     |                     |
 *     +--+--+                +-+---+-+---+              +--+--+
 *     | SUM |                | SUM | SUM |              | SUM |
 *     +-----+                +-----+-----+              +-----+
 *     |  12 |                |  15 |  18 |              |  21 |
 *     +--+--+                +--+--+--+--+              +--+--+
 *        |                      |     |                    |      
 *  +-----+-----+             +--+-----+--+           +-----+-----+
 *  | Process 0 |             | Process 1 |           | Process 2 |
 *  +-----------+             +-----------+           +-----------+
 *  |   Value   |             |   Values  |           |   Value   |
 *  +-----------+             +-----+-----+           +-----------+
 *  |     13    |             |  16 |  20 |           |     23    |
 *  +-----------+             +-----+-----+           +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the size of the communicator
    int size = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Defines my values
    int values[4] = {4 * my_rank, 4 * my_rank + 1, 4 * my_rank + 2, 4 * my_rank + 3};

    // Define the block lengths
    int counts[3] = {1, 2, 1};

    if(my_rank == 1)
    {
        // Each MPI process sends its values and the buffer to receive the corresponding reduction variables
        int reduction_results[2];
        MPI_Request request;
        MPI_Ireduce_scatter(values, reduction_results, counts, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

        // Do some job while it progresses
        // ...

        // Wait for the MPI_Ireduce_scatter to complete
        MPI_Wait(&request, MPI_STATUS_IGNORE);
        printf("[MPI process %d] The sum I received are %d and %d.\n", my_rank, reduction_results[0], reduction_results[1]);
    }
    else
    {
        // Each MPI process sends its values and the buffer to receive the corresponding reduction variables
        int reduction_result;
        MPI_Request request;
        MPI_Ireduce_scatter(values, &reduction_result, counts, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

        // Do some job while it progresses
        // ...

        // Wait for the MPI_Ireduce_scatter to complete
        MPI_Wait(&request, MPI_STATUS_IGNORE);
        printf("[MPI process %d] The sum I received is %d.\n", my_rank, reduction_result);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_reduce_scatter
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking reduce scatter block.
 * @details This application is meant to be run with 3 MPI processes. It
 * consists of a sum reduction; every MPI process has four values to send for
 * reduction. The first values from the MPI process will be reduced and stored 
 * on the MPI process 0. The second and third values will be reduced separately 
 * and stored on MPI process 1, similarly with the fourth values on MPI process
 * 2. It can be visualised as follows:
 *
 *      +---------------+  +---------------+  +---------------+
 *      |   Process 0   |  |   Process 1   |  |   Process 2   |
 *      +---------------+  +---------------+  +---------------+
 *      |     Values    |  |     Values    |  |     Values    |
 *      +---+---+---+---+  +---+---+---+---+  +---+---+---+---+
 *      | 0 | 1 | 2 | 3 |  | 4 | 5 | 6 | 7 |  | 8 | 9 | 10| 11|
 *      +---+---+---+---+  +---+---+---+---+  +---+---+---+---+
 *        |    \   \   \     /  |     |  \      /   /   /   |
 *        | ____\___\___\___/___|_____|___\____/   /   /    |
 *        |/     \   \   \      |     |    \      /   /     |
 *        |       \___\___\____ | ____|_____\____/   /      |
 *        |            \   \   \|/    |      \      /       |
 *        |             \___\___|____ | ______\____/        |
 *        |                  \  |    \|/       \            |
 *        |                   \_|_____|_________\__________ |
 *        |                     |     |                    \|
 *        |                     |     |                     |
 *     +--+--+                +-+---+-+---+              +--+--+
 *     | SUM |                | SUM | SUM |              | SUM |
 *     +-----+                +-----+-----+              +-----+
 *     |  12 |                |  15 |  18 |              |  21 |
 *     +--+--+                +--+--+--+--+              +--+--+
 *        |                      |     |                    |      
 *  +-----+-----+             +--+-----+--+           +-----+-----+
 *  | Process 0 |             | Process 1 |           | Process 2 |
 *  +-----------+             +-----------+           +-----------+
 *  |   Value   |             |   Values  |           |   Value   |
 *  +-----------+             +-----+-----+           +-----------+
 *  |     13    |             |  16 |  20 |           |     23    |
 *  +-----------+             +-----+-----+           +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the size of the communicator
    int size = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Defines my values
    int values[4] = {4 * my_rank, 4 * my_rank + 1, 4 * my_rank + 2, 4 * my_rank + 3};

    // Define the block lengths
    int counts[3] = {1, 2, 1};

    if(my_rank == 1)
    {
        // Each MPI process sends its values and the buffer to receive the corresponding reduction variables
        int reduction_results[2];
        MPI_Request request;
        MPI_Ireduce_scatter(values, reduction_results, counts, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

        // Do some job while it progresses
        // ...

        // Wait for the MPI_Ireduce_scatter to complete
        MPI_Wait(&request, MPI_STATUS_IGNORE);
        printf("[MPI process %d] The sum I received are %d and %d.\n", my_rank, reduction_results[0], reduction_results[1]);
    }
    else
    {
        // Each MPI process sends its values and the buffer to receive the corresponding reduction variables
        int reduction_result;
        MPI_Request request;
        MPI_Ireduce_scatter(values, &reduction_result, counts, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

        // Do some job while it progresses
        // ...

        // Wait for the MPI_Ireduce_scatter to complete
        MPI_Wait(&request, MPI_STATUS_IGNORE);
        printf("[MPI process %d] The sum I received is %d.\n", my_rank, reduction_result);
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_reduce_scatter_block
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use a non-blocking reduce scatter block.
 * @details This application is meant to be run with 3 MPI processes. It
 * consists of a sum reduction; every MPI process has three values to send for
 * reduction. The first values from the MPI process will be reduced and stored 
 * on the MPI process 0. The second values will be reduced separately and stored
 * on MPI process 1, similarly with the third values on MPI process 2. It can be
 * visualised as follows:
 *
 *      +-----------+  +-----------+  +-----------+
 *      | Process 0 |  | Process 1 |  | Process 2 |
 *      +-----------+  +-----------+  +-----------+
 *      |   Values  |  |   Values  |  |   Values  |
 *      +---+---+---+  +---+---+---+  +---+---+---+ 
 *      | 0 | 1 | 2 |  | 3 | 4 | 5 |  | 6 | 7 | 8 |
 *      +---+---+---+  +---+---+---+  +---+---+---+
 *        |    \   \     /   |    \    /   /    |
 *        | ____\___\___/____|_____\__/   /     |
 *        |/     \   \       |      \    /      |
 *        |       \___\_____ | ______\__/       |
 *        |            \    \|/       \         |
 *        |             \____|_________\_______ |
 *        |                  |                 \|
 *        |                  |                  |
 *     +--+--+            +--+--+            +-----+
 *     | SUM |            | SUM |            | SUM |
 *     +-----+            +-----+            +-----+
 *     |  9  |            |  12 |            |  15 |
 *     +--+--+            +--+--+            +--+--+
 *        |                  |                  |      
 *  +-----+-----+      +-----+-----+      +-----+-----+
 *  | Process 0 |      | Process 1 |      | Process 2 |
 *  +-----------+      +-----------+      +-----------+
 *  |   Value   |      |   Value   |      |   Value   |
 *  |     9     |      |     12    |      |     15    |
 *  +-----------+      +-----------+      +-----------+
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the size of the communicator
    int size = 0;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 MPI processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    // Defines my values
    int values[3] = {3 * my_rank, 3 * my_rank + 1, 3 * my_rank + 2};

    // Each MPI process sends its values and the buffer to receive the corresponding reduction variables
    int reduction_result;
    MPI_Request request;
    MPI_Ireduce_scatter_block(values, &reduction_result, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, &request);

    // Do some job while it progresses
    // ...

    // Wait for the MPI_Ireduce_scatter_block to complete
    MPI_Wait(&request, MPI_STATUS_IGNORE);
    printf("[MPI process %d] The sum I received is %d.\n", my_rank, reduction_result);

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_scatterv
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to use the non-blocking variable version of a scatter.
 * @details A process is designed as root and begins with a buffer containig all
 * values, and prints them. It then dispatches these values to all the processes
 * in the same communicator. Other process just receive the dispatched value(s)
 * meant for them. Finally, everybody prints the value received. This
 * application is designed to cover all cases:
 * - Different send counts
 * - Different displacements
 * This application is meant to be run with 3 processes.
 *
 *       +-----------------------------------------+
 *       |                Process 0                |
 *       +-----+-----+-----+-----+-----+-----+-----+
 *       | 100 |  0  | 101 | 102 |  0  |  0  | 103 |
 *       +-----+-----+-----+-----+-----+-----+-----+
 *         |            |     |                |
 *         |            |     |                |
 *         |            |     |                |
 *         |            |     |                |
 *         |            |     |                |
 *         |            |     |                |
 * +-----------+ +-------------------+ +-----------+
 * | Process 0 | |    Process 1      | | Process 2 |
 * +-+-------+-+ +-+-------+-------+-+ +-+-------+-+
 *   | Value |     | Value | Value |     | Value |
 *   |  100  |     |  101  |  102  |     |  103  |
 *   +-------+     +-------+-------+     +-------+ 
 *                
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get number of processes and check that 3 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 3)
    {
        printf("This application is meant to be run with 3 processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Determine root's rank
    int root_rank = 0;

    // Get my rank
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    MPI_Request request;

    switch(my_rank)
    {
        case 0:
        {
            // Define my value
            int my_value;

            // Declare the buffer
            int buffer[7] = {100, 0, 101, 102, 0, 0, 103};

            // Declare the counts
            int counts[3] = {1, 2, 1};

            // Declare the displacements
            int displacements[3] = {0, 2, 6};

            printf("Values in the buffer of root process:");
            for(int i = 0; i < 7; i++)
            {
                printf(" %d", buffer[i]);
            }
            printf("\n");

            // Launch the variable scatter
            MPI_Iscatterv(buffer, counts, displacements, MPI_INT, &my_value, 1, MPI_INT, root_rank, MPI_COMM_WORLD, &request);

            // Do another job while the variable scatter progresses
            // ...

            // Wait for the completion of the variable scatter
            MPI_Wait(&request, MPI_STATUS_IGNORE);
            printf("Process %d received value %d.\n", my_rank, my_value);
            break;
        }
        case 1:
        {
            // Declare my values
            int my_values[2];

            // Launch the variable scatter
            MPI_Iscatterv(NULL, NULL, NULL, MPI_INT, my_values, 2, MPI_INT, root_rank, MPI_COMM_WORLD, &request);

            // Do another job while the variable scatter progresses
            // ...

            // Wait for the completion of the variable scatter
            MPI_Wait(&request, MPI_STATUS_IGNORE);
            printf("Process %d received values %d and %d.\n", my_rank, my_values[0], my_values[1]);
            break;
        }
        case 2:
        {
            // Declare my values
            int my_value;

            // Launch the variable scatter
            MPI_Iscatterv(NULL, NULL, NULL, MPI_INT, &my_value, 1, MPI_INT, root_rank, MPI_COMM_WORLD, &request);

            // Do another job while the variable scatter progresses
            // ...

            // Wait for the completion of the variable scatter
            MPI_Wait(&request, MPI_STATUS_IGNORE);
            printf("Process %d received value %d.\n", my_rank, my_value);
            break;
        }
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}
MPI_ssend
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

/**
 * @brief Illustrates how to send a message in a non-blocking synchronous
 * fashion.
 * @details This program is meant to be run with 2 processes: a sender and a
 * receiver.
 **/
int main(int argc, char* argv[])
{
    MPI_Init(&argc, &argv);

    // Get the number of processes and check only 2 processes are used
    int size;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if(size != 2)
    {
        printf("This application is meant to be run with 2 processes.\n");
        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
    }

    // Get my rank and do the corresponding job
    enum role_ranks { SENDER, RECEIVER };
    int my_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    switch(my_rank)
    {
        case SENDER:
        {
            int buffer_sent = 12345;
            MPI_Request request;
            printf("MPI process %d sends value %d.\n", my_rank, buffer_sent);
            MPI_Issend(&buffer_sent, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
            
            // Do other things while the MPI_Issend completes
            // <...>

            // Let's wait for the MPI_Issend to complete before progressing further.
            MPI_Status status;
            MPI_Wait(&request, &status);
            break;
        }
        case RECEIVER:
        {
            int received;
            MPI_Recv(&received, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            printf("MPI process %d received value: %d.\n", my_rank, received);
            break;
        }
    }

    MPI_Finalize();

    return EXIT_SUCCESS;
}