Introduction
Welcome to the next lesson in our journey through MPI! In our previous lessons, we've focused on point-to-point communication, where one process sends a message to another. You've learned to use both blocking (MPI_Send/MPI_Recv) and non-blocking (MPI_Isend/MPI_Irecv) functions to manage these exchanges, which are the fundamental building blocks of many parallel programs.
However, many parallel algorithms require more structured, group-wide communication patterns. For example:
- How does one process efficiently send the same piece of configuration data to all other processes?
- How do you collect and sum partial results from every process to get a final answer?
While you could implement these patterns using loops of point-to-point messages, it would be tedious, error-prone, and inefficient. MPI provides a powerful and highly optimized set of functions for these exact scenarios, known as collective communication routines.
Today, we will focus on two of the most important collectives. This lesson will teach you how to implement one-to-all broadcast (MPI_Bcast) and all-to-one reduction (MPI_Reduce) operations. Mastering these will significantly simplify your code and improve its performance.
1. What is Collective Communication?
Collective operations involve a group of processes, typically all processes within a communicator like MPI_COMM_WORLD. Unlike point-to-point communication, all processes in the group must call the same collective function.
The key advantage of using MPI's built-in collectives is performance. MPI library implementations often use sophisticated algorithms (like binary trees) to execute these operations far more efficiently than a series of simple sends and receives.
Simple collective communication — Intermediate MPI
To start, let's get a formal introduction to why collective communication is so important. This reading from the ENCCS (EuroCC National Competence Center Sweden) explains the motivation clearly.
Please read the 'Introduction' section. Focus on the distinction between point-to-point and collective messaging and the types of problems collectives are designed to solve.
As you've read, collectives provide a high-level, optimized API for common patterns like broadcast, reduction, and data distribution. Let's dive into the first of these: MPI_Bcast.
2. One-to-All: Broadcasting with MPI_Bcast
A broadcast is a one-to-all communication pattern. A single designated process, the root, has a piece of data that it needs to send to every other process in the communicator.
This is extremely common. Imagine a simulation where the root process (rank 0) reads simulation parameters from a file. It then needs to distribute these parameters to all other worker processes. MPI_Bcast is the perfect tool for this.

The C++ function signature for MPI_Bcast is:
int MPI_Bcast(
void* buffer,
int count,
MPI_Datatype datatype,
int root,
MPI_Comm comm
);
Let's break down the parameters:
buffer: A pointer to the data. For therootprocess, this is the data to be sent. For all other processes, this is where the received data will be stored.count: The number of elements in the buffer.datatype: The MPI data type of the elements (e.g.,MPI_INT,MPI_DOUBLE).root: The rank of the process that is sending the data.comm: The communicator (e.g.,MPI_COMM_WORLD).
Crucially, all processes must call MPI_Bcast with the same root, count, datatype, and comm. The buffer is an input argument on the root and an output argument for everyone else.
Simple collective communication — Intermediate MPI
The ENCCS guide provides a detailed breakdown of MPI_Bcast.
Please read the 'Broadcast' section. Pay close attention to the parameter descriptions and the fact that the call is blocking and synchronous.
Here is a simple example in C++. Let's say rank 0 decides on a value, and we want every process to have it.
#include <iostream>
#include <mpi.h>
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
int shared_value;
int root_rank = 0;
// Rank 0 sets the initial value. For all other ranks, 'shared_value' is uninitialized.
if (world_rank == root_rank) {
shared_value = 42;
std::cout << "Process 0 is broadcasting the value " << shared_value << std::endl;
}
// All processes call MPI_Bcast.
// Rank 0 sends the value from 'shared_value'.
// All other ranks receive the value into 'shared_value'.
MPI_Bcast(&shared_value, 1, MPI_INT, root_rank, MPI_COMM_WORLD);
// Now, every process has the value 42.
std::cout << "Process " << world_rank << " received the value " << shared_value << std::endl;
MPI_Finalize();
return 0;
}
3. All-to-One: Aggregation with MPI_Reduce
A reduction is an all-to-one communication pattern. It takes data from every process, combines it using a specified operation (like sum, max, or product), and places the final result on the root process.
This is essential for parallel algorithms that involve aggregation. For example, if each process calculates a partial sum of a large array, MPI_Reduce can be used to compute the global sum.

The C++ function signature for MPI_Reduce is:
int MPI_Reduce(
const void* sendbuf,
void* recvbuf,
int count,
MPI_Datatype datatype,
MPI_Op op,
int root,
MPI_Comm comm
);
Parameters:
sendbuf: A pointer to the local data that each process contributes.recvbuf: A pointer to where the final result will be stored. This is only used by therootprocess. Other processes can passnullptr(or any valid pointer, as it will be ignored).count: The number of elements in the send/receive buffers.datatype: The MPI data type of the elements.op: The reduction operation to apply (e.g.,MPI_SUM,MPI_MAX,MPI_MIN,MPI_PROD).root: The rank of the process that will receive the final result.comm: The communicator.
Parallel C++: MPI Collective Communication
Let's watch a practical demonstration of implementing a sum reduction. This video from Nick's 'Parallel C++' series walks through a complete example, which will solidify your understanding of how MPI_Reduce is used in code.
Watch the segment from 08:01 to 10:28. The presenter explains the parameters of MPI_Reduce in the context of summing partial results and clarifies the role of the send and receive buffers.
As the video showed, MPI_Reduce elegantly replaces a manual, cumbersome loop of sends and receives for aggregation tasks.
For a quick reference, here are some of the most common reduction operations available.
The MolSSI Education project provides a concise table of available reduction operations.
Review the first part of this resource, focusing on the table that lists the different values for the op parameter, such as MPI_MAX, MPI_SUM, and MPI_PROD.
4. Hands-on Practice: Calculating
Now it's time to put these concepts into practice with a classic numerical problem. We'll calculate using numerical integration. The formula is:
We can approximate this integral by summing the areas of many small rectangles under the curve. This is a perfect task to parallelize!
Your Task:
Write a C++ MPI program that:
- On the root process (rank 0), defines the total number of rectangles (e.g.,
n = 1000000). - Uses
MPI_Bcastto sendnfrom the root to all other processes. - Each process calculates a partial sum of the integral over its own assigned range of rectangles. For
pprocesses, processishould handle rectangles fromi*n/pto(i+1)*n/p - 1. - Each process computes its partial sum. The width of each rectangle is
1.0/n. The height is given by the function at the midpoint of the rectangle's base. - Uses
MPI_Reducewith theMPI_SUMoperation to add up all the partial sums into a finalpivalue on the root process. - The root process prints the calculated value of and the difference from the true value.
// Helper code snippet for step 4
double h = 1.0 / n;
double local_sum = 0.0;
for (int i = my_first_rect; i < my_last_rect; ++i) {
double x = h * (i + 0.5);
local_sum += 4.0 / (1.0 + x*x);
}
double partial_pi = h * local_sum;
Take about 15-20 minutes to implement this. This exercise combines both MPI_Bcast and MPI_Reduce into a single, cohesive program.
Simple collective communication — Intermediate MPI
The ENCCS guide has an exercise for this exact problem, including a code scaffold and a solution if you need guidance.
Navigate to the section 'Exercise: calculating π using numerical integration'. You can use the provided code scaffold in the content/code/day-1/09_integrate-pi folder as a starting point. Try to complete the program on your own, but refer to the solution if you get stuck.
Conclusion
In this lesson, you've moved beyond simple point-to-point messaging and learned to use MPI's powerful collective communication routines. These functions are not just convenient; they are essential for writing clean, scalable, and high-performance parallel code.
Key Takeaways:
- Collective vs. Point-to-Point: Collectives involve a group of processes, are synchronous, and are highly optimized for common communication patterns.
MPI_Bcast(One-to-All): Used to distribute the same data from a singlerootprocess to all other processes in a communicator.MPI_Reduce(All-to-One): Used to gather data from all processes, combine it with a specified operation (MPI_SUM,MPI_MAX, etc.), and deliver the final result to a singlerootprocess.- Synchronous Nature: All processes in the communicator must call the collective function, and the function will block until the operation is complete for the group.
Preview of the Next Lesson
So far, MPI_Bcast sends the same data to every process. But what if you have a large array and want to give each process a unique chunk of it to work on? This data distribution pattern is handled by MPI_Scatter. Its counterpart, MPI_Gather, collects these unique chunks back into a single array on the root process. In our next lesson, we will explore these two essential collective operations.