Kahibaro
Discord Login Register

Communicators

Understanding Communicators in MPI

In MPI, communicators define which processes can talk to each other, and how they are grouped. Almost everything you do with MPI uses a communicator, whether you notice it or not.

This chapter focuses on:

We assume basic familiarity with MPI processes and simple point-to-point / collective operations from previous sections.


What is a Communicator?

Conceptually, a communicator is:

Most MPI calls take a communicator argument, for example:

The communicator:

The default communicator you almost always start with is:

Groups vs Communicators

MPI distinguishes between:

Typical workflow:

  1. Start from the group of an existing communicator (often MPI_COMM_WORLD).
  2. Manipulate that group to build subgroups.
  3. Build new communicators from those groups.

Essential API pieces (conceptual, not full details):

Groups determine membership; communicators determine membership + messaging context.


Intracommunicators and Intercommunicators

MPI defines two main kinds of communicators:

Intracommunicators

Examples:

Almost all introductory MPI code uses intracommunicators.

Intercommunicators

For a beginner course, it’s enough to recognize that:

Creating Communicators: Custom Subsets of Processes

Using only MPI_COMM_WORLD is simple but often suboptimal. Many algorithms need subgroups of processes to coordinate independently. Communicators are how you express that in MPI.

Why Create New Communicators?

Typical reasons:

Basic Pattern: From Group to Communicator

General procedure:

  1. Get group of a starting communicator (often MPI_COMM_WORLD).
  2. Create a subgroup.
  3. Create a communicator from the subgroup.

Illustrative example (conceptual):

MPI_Comm world = MPI_COMM_WORLD;
MPI_Comm subcomm;
MPI_Group world_group, sub_group;
MPI_Comm_group(world, &world_group);
// Suppose we want a communicator containing ranks 0..3 only
int ranks[4] = {0, 1, 2, 3};
MPI_Group_incl(world_group, 4, ranks, &sub_group);
// Create communicator for that group
MPI_Comm_create(world, sub_group, &subcomm);
// Processes not in sub_group get MPI_COMM_NULL in subcomm

Key points:

Splitting Communicators: `MPI_Comm_split`

MPI_Comm_split is the main, high-level tool for building subcommunicators from an existing one.

Conceptually, every process in the original communicator calls:

MPI_Comm_split(old_comm, color, key, &new_comm);

Parameters:

Example: Split by Even/Odd Rank

Suppose we want:

int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
int color = world_rank % 2; // 0 for even ranks, 1 for odd
MPI_Comm even_odd_comm;
MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &even_odd_comm);
// Now there are two communicators:
// - color 0: ranks {0, 2, 4, ...} (with new ranks 0..neven-1)
// - color 1: ranks {1, 3, 5, ...} (with new ranks 0..nodd-1)

Within each of these new communicators:

refer to the size and rank inside that subcommunicator, not in MPI_COMM_WORLD.

Example: Split by Role

Common pattern: assign roles to processes and create a communicators per role.

// For example, rank 0 is "I/O master", others are "compute"
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
int color;
if (world_rank == 0) {
    color = 0;  // IO group
} else {
    color = 1;  // compute group
}
MPI_Comm role_comm;
MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &role_comm);
// Now you can do collectives independently within IO or compute groups.

This is useful for:

Duplicating Communicators: `MPI_Comm_dup`

Different parts of a code (or different libraries) may want to use the same set of processes but avoid interfering with each other’s messages or collectives. MPI_Comm_dup creates a new communicator with:

Conceptually:

MPI_Comm new_comm;
MPI_Comm_dup(old_comm, &new_comm);

Properties:

Typical use:

Usage pattern in a library (simplified):

void my_library_init(MPI_Comm user_comm, my_lib_handle *h)
{
    MPI_Comm_dup(user_comm, &h->lib_comm);
    // Now library uses h->lib_comm for all MPI calls
}

Using `MPI_COMM_NULL`

Some communicator creation calls may yield MPI_COMM_NULL on some ranks:

Any MPI calls that expect a valid communicator must not be given MPI_COMM_NULL. Always check:

if (new_comm != MPI_COMM_NULL) {
    MPI_Comm_rank(new_comm, &subrank);
    MPI_Comm_size(new_comm, &subsize);
    // safe MPI operations on new_comm
}

This allows you to:

Practical Use Cases for Custom Communicators

1. Decomposing the Domain

In structured grid problems, you might:

Communicators encode the geometry of your process layout, matching the problem geometry.

2. Overlapping Multiple Algorithms

A single MPI job may run multiple algorithms in parallel, each on a subset of processes:

Separate communicators avoid conflicts when each algorithm calls its own collectives or uses overlapping tags.

3. Resource-Aware Grouping

You can align communicators with hardware resources:

This supports node-local or socket-local collectives, which may be more efficient (e.g., shared-memory aware operations).

4. Modular Software Design

Large codes composed of multiple modules or libraries often:

Communicators thus support composability: independent pieces of code use MPI safely in the same job.


Communicators and Collectives

Collective operations are always scoped to a communicator:

This implies:

Example pattern:

  1. Build subcommunicator for the relevant subset.
  2. Call collective only on that communicator.
// Suppose subcomm contains only the processes that need to participate
if (subcomm != MPI_COMM_NULL) {
    MPI_Bcast(data, count, MPI_DOUBLE, root_in_subcomm, subcomm);
}

This is central to writing correct and scalable distributed-memory codes.


Lifetime and Cleanup

Communicators are MPI objects that you should free when no longer needed:

MPI_Comm_free(&subcomm);

Notes:

MPI_COMM_WORLD and certain predefined communicators are managed by MPI and must not be freed.


Summary

Views: 13

Comments

Please login to add a comment.

Don't have an account? Register now!