Help with parfor progress bar using data queue
22 views (last 30 days)
Show older comments
Hello. I am trying to implement a progress par for a parfor loop, following the example shown at
I am using a script instead of a function as shown in the example, but I am unable to adapt the code in a way that works.
My code:
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
afterEach(D, @nUpdateWaitbar);
num_files = 1000;
parfor i = 1:num_files
% do stuff
send(D, [i num_files]);
end
function p = nUpdateWaitbar(input)
p = input(1)/input(2)
waitbar(p, h);
end
Executing this gives
Warning: Unrecognized function or variable 'h'.
which I assume is because the function nUpdateWaitbar doesnt know what 'h' is
but if I try to pass the waitbar handle 'h' into the dataqueue, I get another error:
Cannot convert double value 1000 to a handle
Can you please point me towards what I am doing wrong?
the parfor loop runs fine without the waitbar code so I dont think that is the issue
thanks!
5 Comments
Mario Malic
on 24 Nov 2020
Edited: Mario Malic
on 25 Nov 2020
Try this
function p = nUpdateWaitbar(input)
persistent h
if isempty(h)
h = findall(0, 'type', 'figure', 'Tag', 'TMWWaitbar');
end
p = input(1)/input(2)
waitbar(p, h);
end
Accepted Answer
Edric Ellis
on 25 Nov 2020
The problem here is that the documentation example is using a nested function, which is able to acces variables in the containing workspace. Your function definition inside a script is a local function, which cannot automatically access the variables in the containing workspace. An alternative fix is to use an anonymous function handle to "bind" in the value of h.
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
% Note anonymous function captures the value of "h" to pass
% in to "nUpdateWaitbar"
afterEach(D, @(data) nUpdateWaitbar(data, h));
num_files = 1000;
parfor i = 1:num_files
% do stuff
send(D, [i num_files]);
end
function p = nUpdateWaitbar(input, h)
p = input(1)/input(2)
waitbar(p, h);
end
This "works", but it isn't right. If you run it you'll see the value flickering about because the parfor loop iterations are not processed in order. So, you need a different approach for nUpdateWaitbar. I would essentially make nUpdateWaitbar contain persistent data and have an "initialisation" mode. Like this:
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
num_files = 1000;
% Dummy call to nUpdateWaitbar to initialise
nUpdateWaitbar(num_files, h);
% Go back to simply calling nUpdateWaitbar with the data
afterEach(D, @nUpdateWaitbar);
parfor i = 1:num_files
% do stuff
% Note we send only an "increment" for the waitbar.
send(D, 1);
end
function p = nUpdateWaitbar(data, h)
persistent TOTAL COUNT H
if nargin == 2
% initialisation mode
H = h;
TOTAL = data;
COUNT = 0;
else
% afterEach call, increment COUNT
COUNT = 1 + COUNT;
p = COUNT / TOTAL;
waitbar(p, H);
end
end
3 Comments
Edric Ellis
on 30 Nov 2020
persistent is more tightly-scoped than global - so you don't have to worry about whether other bits of code might interfere. (In general, it's good practice to try and keep your data as tightly-scoped as possible). In my 2nd example above, nUpdateWaitbar is a "local" function inside the script - this means it is only accessible to code within that script file.
More Answers (0)
See Also
Categories
Find more on Parallel for-Loops (parfor) in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!