Go is a powerful and versatile programming language, known for its simplicity, efficiency, and modern features. This has made it a popular choice for concurrent and distributed systems. In this article, we will introduce MPB (Message Passing Bridge), a library that enables the use of the Message Passing Interface (MPI) in Go programs.
MPI is a widely-used communication protocol for parallel computing that allows processes to exchange data using messages. It provides an efficient and portable way to build distributed and high-performance computing applications. By combining the power of Go and MPI through the MPB library, we can create scalable parallel solutions.
To follow this guide, you should have:
To get started with MPB, you need to install the library first. You can do this using the go get
command:
go get github.com/btracey/go-mpb
This command will download and install the MPB package to your Go workspace.
Now that we have MPB installed, let’s write a simple Go program that uses the library to send and receive messages between processes. We will create a program that distributes an array of integers among multiple processes, computes the sum of the elements in each process, and then gathers the partial sums.
First, we need to import the necessary packages, including the MPB library:
package main
import (
"fmt"
"github.com/btracey/go-mpb"
"github.com/btracey/go-mpb/mpbcomm"
)
Before using any MPI functions, we need to initialize the MPI environment and finalize it when we finish:
func main() {
mpi := mpb.New(mpbcomm.New())
err := mpi.Init()
if err != nil {
panic(err)
}
defer mpi.Finalize()
}
Each process in an MPI program has a unique identifier called its rank. We also need to know the total number of processes, known as the size. We can obtain these values with the following code:
rank, err := mpi.Rank()
if err != nil {
panic(err)
}
size, err := mpi.Size()
if err != nil {
panic(err)
}
Now, we can distribute the data and compute the partial sums. We will use the MPI_Scatter
and MPI_Gather
functions to distribute and gather the data, respectively. In this example, we will assume that the array has a length that is a multiple of the number of processes:
// Create the input data (only on the root process)
data := make([]int, size*4)
if rank == 0 {
for i := range data {
data[i] = i + 1
}
}
// Prepare the buffers for receiving the scattered data
localData := make([]int, len(data)/size)
// Scatter the data among the processes
err = mpi.Scatter(data, localData, 0) // 0 is the root process
if err != nil {
panic(err)
}
// Compute the local sum
localSum := 0
for _, value := range localData {
localSum += value
}
We can now gather the partial sums and compute the total sum:
// Prepare the buffer for gathering the partial sums
partialSums := make([]int, size)
// Gather the partial sums on the root process
err = mpi.Gather(localSum, partialSums, 0) // 0 is the root process
if err != nil {
panic(err)
}
// Compute the total sum (only on the root process)
if rank == 0 {
totalSum := 0
for _, sum := range partialSums {
totalSum += sum
}
fmt.Printf("Total sum: %d\n", totalSum)
}
With these steps, we have created a simple Go program that uses the MPB library and MPI to distribute data and compute a sum in parallel. The complete source code is provided below:
package main
import (
"fmt"
"github.com/btracey/go-mpb"
"github.com/btracey/go-mpb/mpbcomm"
)
func main() {
mpi := mpb.New(mpbcomm.New())
err := mpi.Init()
if err != nil {
panic(err)
}
defer mpi.Finalize()
rank, err := mpi.Rank()
if err != nil {
panic(err)
}
size, err := mpi.Size()
if err != nil {
panic(err)
}
// Create the input data (only on the root process)
data := make([]int, size*4)
if rank == 0 {
for i := range data {
data[i] = i + 1
}
}
// Prepare the buffers for receiving the scattered data
localData := make([]int, len(data)/size)
// Scatter the data among the processes
err = mpi.Scatter(data, localData, 0)
if err != nil {
panic(err)
}
// Compute the local sum
localSum := 0
for _, value := range localData {
localSum += value
}
// Prepare the buffer for gathering the partial sums
partialSums := make([]int, size)
// Gather the partial sums on the root process
err = mpi.Gather(localSum, partialSums, 0)
if err != nil {
panic(err)
}
// Compute the total sum (only on the root process)
if rank == 0 {
totalSum := 0
for _, sum := range partialSums {
totalSum += sum
}
fmt.Printf("Total sum: %d\n", totalSum)
}
}
To run the program, you need to compile it first. You can use the go build
command to create an executable:
go build mpi_sum.go
Now you can run the program using an MPI launcher, such as mpirun
or mpiexec
. For example, to run the program with four processes, use the following command:
mpirun -np 4 ./mpi_sum
This will execute the program and display the total sum of the array elements.
In this article, we have presented an introduction to using the MPB library for Go, which enables the use of the Message Passing Interface in Go programs. With MPB, you can leverage the powerful features of both Go and MPI to create efficient and scalable parallel solutions. The example provided demonstrates how to use MPB for a simple distributed sum computation, but the library can be applied to a wide range of parallel computing tasks.