Leveraging Bodo, PyTorch, and MPI for Efficient CPU-GPU Distributed Training in Large-Scale Machine Learning

April 9, 2025

hadia-ahmed

Training deep learning models efficiently isn’t just about powerful GPUs—it starts with how you move and prepare your data. In this post, we’ll explore how to create a flexible system capable of scaling across heterogeneous CPU and GPU environments. Using Bodo and its autoparallelizing inferential compiler,  we’ll preprocess data on CPUs at scale and send it in batches to GPUs for distributed training in PyTorch.

Bodo is designed to enable scalable and efficient parallel computing using Python. By leveraging Bodo’s capabilities to preprocess and batch distributed data on CPUs before passing it to GPUs, we can maximize computational efficiency and training speed. This approach is especially beneficial for handling large datasets that don’t fit entirely on a single GPU.

Setting Up Development Environments 

1. Create Conda Environment

2. Install Bodo

NOTE: Bodo will install MPI as part of its dependencies.

3. Install PyTorch + MPI

PyTorch has to be installed from source to enable using MPI as the backend

An Integrated Approach: Bodo, PyTorch, and MPI

Our architecture leverages three powerful technologies in combination:

1. Bodo: High-Performance Parallel Computing

Bodo is a parallel computing framework that enables significant speedups for Python data science workloads. Key benefits include:

  • JIT compilation of Python code for high performance
  • Automatic data distribution and parallelization of ETL operations across CPUs
  • Seamless integration with existing Python data science tools

2. PyTorch: Flexible Deep Learning

PyTorch provides the deep learning foundation with:

  • Dynamic computation graphs for flexible model development
  • Distributed training capabilities via torch.distributed
  • GPU acceleration for tensor operations
  • Comprehensive optimization algorithms

3. MPI (Message Passing Interface): Robust Communication

MPI serves as the communication backbone, offering:

  • Efficient message passing between distributed processes
  • Established standards for high-performance computing
  • Support for complex network topologies

System Architecture Overview

We begin by initializing the process group using PyTorch’s distributed package. The script uses MPI as the backend for process group initialization, which works seamlessly with Bodo’s parallel capabilities.

Next, we determine the GPU ranks for each process to ensure that each GPU gets assigned the appropriate data batches. The function get_gpu_ranks uses MPI to collect GPU availability information and distribute GPU resources efficiently.

Preprocessing with Bodo

Efficient data loading and preprocessing are crucial for large datasets. Bodo’s JIT compilation significantly speeds up this step. In our example, we load the NYC taxi dataset, preprocess it using Scikit-learn’s StandardScaler, and transform the features:

Sending Data in Batches to GPUs

A key aspect of this setup is sending data from CPUs to GPUs in batches to optimize memory usage. The batch_generator function uses Bodo’s rebalance to efficiently transfer batches to designated GPU ranks.

Overlapping Batch Preparation with Training

One of the key advantages of using the yield keyword in the batch_generator function is that it allows for overlapping batch preparation with model training. Since yield makes the generator function produce one batch at a time, the model can start training on the current batch while the next batch is being prepared. This significantly reduces idle time and increases the efficiency of the data pipeline, especially in distributed settings where data loading can become a bottleneck.

Training the Neural Network

The model architecture consists of a simple feedforward neural network. We distribute the model across available GPUs using DistributedDataParallel, allowing for synchronized training across multiple devices:

The TaxiDemandNN model is a simple feedforward neural network that aims to train a neural network model to predict the trip_miles (how far a taxi will travel on future trips) based on features like pickup/dropoff locations, trip time, and fare details using NYC taxi data. 

The training loop efficiently batches data using the generator function and processes it on the assigned GPU. By using Bodo to preprocess and rebalance the data across multiple GPUs, we reduce data transfer bottlenecks and increase training throughput.

Key Takeaways

  • Bodo accelerates data preprocessing and batch distribution, making it a powerful tool for distributed deep learning.
  • The combination of Bodo with PyTorch enables efficient training by leveraging both CPU and GPU resources.
  • Sending data in batches to GPUs minimizes memory constraints and optimizes training speed.
  • Using `yield` in the batch generator function allows for overlapping batch preparation with training, reducing idle time.

By combining Bodo’s high-performance CPU-based preprocessing with PyTorch’s flexible GPU-based training, we’ve demonstrated a scalable and efficient architecture for large-scale machine learning. No more bottlenecks, memory meltdowns, or waiting around for data to catch up. This setup is flexible, scalable, and surprisingly elegant. Whether you’re scaling up an existing project or building something brand new, this approach gives you the flexibility and efficiency to move quickly and build smarter. 

Want to try it yourself? 

  • Install Bodo: pip install bodo
  • Grab the full script here

Join our Slack community to connect with other users, share use cases, and stay up-to-date on the latest from Bodo.

const next = await fetch("https://api.example.com/next-section");
Black and white grid pattern with black dots at the intersections, forming a repeating checkered design.
const next = await fetch("https://api.example.com/next-section");
Black and white grid pattern with black dots at the intersections, forming a repeating checkered design.