How to Add a Progress Bar for Matlab parfor Loops
An easy way to track progress in parallel for loops in Matlab
Running parallelized Monte Carlo simulations in Matlab is easy with parfor
loops. But there’s a catch: how can you know how much of the work is done and how much is left?
This post is about an efficient and easy visual way to keep track of progress in parfor
loop (and some less obvious Matlab features I learned about when trying to figure it out).
Contents
- Introduction
- The Challenge with Tracking
parfor
Progress - The Solution: A Parallel Progress Bar Function (by Example)
- Going Deeper: How Does
createParallelProgressBar
Work?
The Challenge: Tracking parfor
Progress
Tracking progress in a regular for
loop is straightforward because each iteration runs sequentially. Simply dividing the current iteration number by the total number of iterations gives an accurate estimate of progress.
However, parfor
loops work differently. Matlab uses dynamic scheduling to assign iterations to workers as they finish their previous tasks. This means that iterations are not completed in a simple (or even a deterministic!) order. As a result, iteration numbers alone do not reflect total progress accurately.
The Solution: A Parallel Progress Bar Function
The solution? A visual progress bar that uses the messages each worker sends back to the central process! Matlab’s parallel.pool.DataQueue
allows us to listen to such messages and gather progress updates.
With this approach, the createParallelProgressBar
function tracks parfor
progress with just two lines of extra code in the script.
Here’s how to use createParallelProgressBar
in a parallelized loop:
- Download or copy the
createParallelProgressBar
function and place it somewhere along your Matlabpath
. - Insert into your code the two lines marked with
<---
, as shown in the example.
An example:
numSamples = 100;
% Initialize the progress bar
queue = createParallelProgressBar(numSamples); % <---
parfor iterID = 1:numSamples
% Simulate computation, replace with your own
pause(rand(1));
% Update progress bar after simulation is done
send(queue, iterID); % <---
end
The code for createParallelProgressBar
is as follows:
function queue = createParallelProgressBar(totalIterations)
% createParallelProgressBar Initializes a progress bar for parallel
% computations with dynamic color changing from dark orange to blue.
%
% Args:
% totalIterations (int): Total number of iterations for the
% progress bar.
%
% Returns:
% queue (parallel.pool.DataQueue): DataQueue to receive progress
% updates.
%
% Example usage in a parallel loop:
% numSamples = 100;
% % Create progress bar
% queue = createParallelProgressBar(numSamples);
% parfor i = 1:numSamples
% % Simulate computation
% pause(0.1);
% % Update progress bar
% send(queue, i);
% end
% Initialize DataQueue and Progress Bar
queue = parallel.pool.DataQueue;
progressBar = waitbar(0, 'Processing...', 'Name', 'Computation Progress');
% Access the Java-based components of the waitbar
barChildren = allchild(progressBar);
javaProgressBar = barChildren(1).JavaPeer; % Access the Java progress bar
% Enable string painting to show percentage inside the bar
javaProgressBar.setStringPainted(true);
% Reset persistent variable count
persistent count
count = 0;
% Define colors between which the bar interpolates
colorEnd = [12, 123, 220] / 255; % light blue
colorStart = [171, 94, 0] / 255; % brown-orange
% Nested function to update progress and color
function updateProgress(~)
count = count + 1;
shareComplete = count / totalIterations;
% Update waitbar position
waitbar(shareComplete, progressBar);
% Calculate color transition
currentColor = (1 - shareComplete) * colorStart + ...
shareComplete * colorEnd;
red = currentColor(1);
green = currentColor(2);
blue = currentColor(3);
% Convert RGB triplet to Java Color
javaColor = java.awt.Color(red, green, blue);
% Set the progress bar color
javaProgressBar.setForeground(javaColor);
% Close progress bar when complete
if count == totalIterations
close(progressBar);
count = [];
end
end
% Add listener to the DataQueue
afterEach(queue, @updateProgress);
end
The solution is based on the template given in the official documentation for parallel.pool.DataQueue
. It adds some nice features, both aesthetic (explicit percentage progression, changing colors) and more substantial (automatic clean-up after completion). Tested with Matlab 2024b on Windows.
Going Deeper: How Does createParallelProgressBar
Work?
How does this progress bar work? Overall, the flow is:
- Initialize:
createParallelProgressBar
initializes aDataQueue
,waitbar
, and apersistent
count
variable. - Create Listener: It attaches the
updateProgress
function as a listener to theDataQueue
, so that each message from the workers will trigger a progress update. - Run in Parallel: As the
parfor
loop runs, each worker completes iterations, sending messages to theDataQueue
. - Update Progress Bar: Each message received by
DataQueue
triggersupdateProgress
, incrementingcount
and updating the waitbar to show real-time progress. - Complete and Close: When all iterations are done,
count
matchestotalIterations
, and the waitbar is closed automatically.
Going a little deeper into each component here:
1. DataQueue and Worker Communication
The DataQueue
object enables communication between parfor
workers and the main process. Each worker sends a message to DataQueue
when it completes an iteration, allowing us to track progress:
queue = parallel.pool.DataQueue;
Each time a worker completes a loop iteration, we will send a message to the DataQueue
in the main file.
2. Creating and Initializing the Waitbar
waitbar
is a function that creates a pop-up window with a horizontal wait bar. It is initialized with 0
progress made, some message ('Processing...'
), and a title ('Computation Progress'
):
progressBar = waitbar(0, 'Processing...', 'Name', 'Computation Progress');
3. Using Persistent Variables for State Tracking
We use a persistent
variable to keep track of how many iterations have been completed up to a given moment. persistent
variables retain their value across multiple calls to the function. Unlike global
variables, such variables are local to the function in which they are declared.
Specifically, we declare a persistent
variable count
:
persistent count
count = 0;
We initialize count
to 0
and increment it by 1
each time an iteration is completed.
4. Updating and Closing the Waitbar with a Nested Function
The nested function updateProgress
is responsible for is called each time a message is received by the DataQueue
from a worker.
function updateProgress(~)
count = count + 1;
shareComplete = count / totalIterations;
% Update waitbar position
waitbar(shareComplete, progressBar);
% Omitted color computations, see below
% <..>
% Close progress bar when complete
if count == totalIterations
close(progressBar);
count = [];
end
end
Every time updateProgress
is called, it increments count
by 1
. Then the progress on waitbar
is updated based on the new value of count
.
Once count
reaches totalIterations
, all tasks are complete. Then the waitbar is closed, and count
is reset.
5. Attaching a Listener to the DataQueue
We need to make sure that the updateProgress
is called every time an iteration is finished. We create a suitable listener:
afterEach(queue, @updateProgress);
To make use of this listener, we insert the following sender line in the main script
send(queue, i)
Effectively, every time a worker completes an iteration and sends a message via send(queue, i)
, Matlab calls updateProgress
in the main session to update the progress bar.
6. Customizing the Progress Bar
MATLAB’s built-in waitbar
function has a very limited set of options for styling. To change the bar color with progress, we access the underlying Java JProgressBar
that MATLAB uses to render waitbar
:
barChildren = allchild(progressBar);
javaProgressBar = barChildren(1).JavaPeer;
The line javaProgressBar = wbc(1).JavaPeer
retrieves the Java-based waitbar
component which we can then modify.
javaProgressBar.setStringPainted(true);
This line enables the display of a percentage or other string inside the progress bar. With this line, we can then manipulate the color of the bar by passing an RGB triplet using java.awt.Color
:
javaColor = java.awt.Color(red, green, blue);
javaProgressBar.setForeground(javaColor);
The color transition goes from dark orange to light blue as progress moves from 0% to 100%, with the colors chosen to be as accessible as possible.
Conclusion
With createParallelProgressBar
, you can easily track progress in parfor
loops. Using DataQueue
for worker communication and accessing Java properties for color customization gives a lightweight and visually informative solution.