Matlab: прогресс печати из цикла parfor
Я запускаю много длительных симуляций в Matlab, обычно занимая от нескольких минут до нескольких часов, поэтому, чтобы ускорить работу, я решил одновременно запускать симуляции, используя цикл parfor
.
arglist = [arg1, arg2, arg3, arg4];
parfor ii = 1:size(arglist, 2)
myfun(arglist(ii));
end
Все работало отлично, за исключением одного: прогресс печати. Поскольку каждый симулятор занимает много времени, я обычно печатаю прогресс, используя что-то вроде
prevlength = 0;
for ii = 1:tot_iter
% Some calculations here
msg = sprintf('Working on %d of %d, %.2f percent done', ii, tot_iter, ii/tot_iter);
fprintf(repmat('\b', 1, prevlength))
fprintf(msg);
prevlength = numel(msg);
end
но, как и следовало ожидать, при выполнении этого внутри цикла parfor
вы получаете хаос.
Я много искал поисков решения и нашел кучу "принтеров для достижения прогресса", например этот. Тем не менее, все они печатают ход всего цикла parfor
, а не показывают, как далеко продвинулась каждая из отдельных итераций. Поскольку в цикле parfor
есть только 4-8 итераций, но каждая итерация занимает около часа, этот подход мне не очень помогает.
Идеальное решение для меня было бы чем-то вроде этого
Working on 127 of 10000, 1.27 percent done
Working on 259 of 10000, 2.59 percent done
Working on 3895 of 10000, 38.95 percent done
Working on 1347 of 10000, 13.47 percent done
то есть каждое симуляция получает одну строку, показывающую, как далеко она прошла. Я не уверен, хотя, если это вообще возможно, я, по крайней мере, не могу представить, как это сделать.
Другим способом было бы сделать что-то вроде этого
Sim 1: 1.27% Sim 2: 2.59% Sim 3: 38.95% Sim 4: 13.47%
то есть показать все прогрессии в одной строке. Чтобы сделать это, вам нужно будет отслеживать, какую позицию на каждой симуляции нужно написать и написать там, не стирая другие прогрессии. Я не могу понять, как это будет сделано, возможно ли это сделать?
Если есть какое-то другое решение моей проблемы (показывающее прогресс каждой отдельной итерации), о котором я не думал, я был бы рад услышать об этом.
Поскольку это первый раз, когда я задаю здесь вопрос о SO, вполне возможно, что я кое-что пропустил; если да, пожалуйста, не стесняйтесь комментировать ниже.
Изменить
Получив этот ответ, я подумал, что должен поделиться тем, как я использовал его для решения моей проблемы, поскольку я не использовал его точно так же, как в ответе, в случай, когда кто-то другой сталкивается с той же проблемой.
Вот небольшая тестовая программа с той же структурой, что и моя программа, используя индикатор выполнения (parfor_progress
), упомянутый в ответе:
function parfor_progress_test()
cpus = feature('numCores');
matlabpool('open', cpus);
cleaner = onCleanup(@mycleaner);
args = [1000, 1000, 1000, 1000];
m = sum(args);
parfor_progress(m);
parfor ii = 1:size(args,2)
my_fun(args(ii));
end
parfor_progress(0);
end
function my_fun(N)
for ii = 1:N
pause(rand*0.01);
parfor_progress;
end
end
function mycleaner
matlabpool close;
fclose all;
end
Ответы
Ответ 1
Простой индикатор выполнения
Что-то вроде индикатора выполнения может быть сделано аналогично этому...
Перед циклом parfor
:
fprintf('Progress:\n');
fprintf(['\n' repmat('.',1,m) '\n\n']);
И во время цикла:
fprintf('\b|\n');
Здесь m
- общее число итераций, .
показывает общее количество итераций, а |
показывает количество завершенных итераций. \n
гарантирует, что символы будут напечатаны в цикле parfor
.
Индикатор выполнения и процентное заполнение
В противном случае вы можете попробовать следующее: http://www.mathworks.com/matlabcentral/fileexchange/32101-progress-monitor--progress-bar--that-works-with-parfor
Будет отображаться индикатор выполнения и процентное завершение, но его можно легко изменить, чтобы просто включить процентное завершение или индикатор выполнения.
Эта функция изменяет символ на файл на каждой итерации, а затем считывает количество символов, записанных в этот файл, что указывает количество завершенных итераций. Этот способ доступа к файлу разрешен в parfor
.
Предполагая, что вы правильно добавили выше на свой путь MATLAB, вы можете использовать следующее:
arglist = [arg1, arg2, arg3, arg4];
parfor_progress(size(arglist, 2)); % Set the total number of iterations
parfor ii = 1:size(arglist, 2)
myfun(arglist(ii));
parfor_progress; % Increment the progress counter
end
parfor_progress(0); % Reset the progress counter
Время завершения и процентное завершение
Существует также функция, называемая showTimeToCompletion()
, которая доступна из: https://www.soundzones.com/software/sound-zone-tools/
и работает рядом с parfor_progress
. Эта функция позволяет вам распечатать подробную сводку о прогрессе цикла parfor (или любого цикла в этом отношении), который содержит время начала, продолжительность работы, расчетное время окончания и процентное завершение. Он использует интеллектуальное использование символа \b
(backspace), чтобы окно команды не заливалось текстом. Хотя это не просто "бар" прогресса, возможно, более информативный.
Третий пример в заголовке файла функции,
fprintf('\t Completion: ');
showTimeToCompletion; startTime=tic;
len=1e2;
p = parfor_progress( len );
parfor i = 1:len
pause(1);
p = parfor_progress;
showTimeToCompletion( p/100, [], [], startTime );
end
выводит в командное окно следующее:
Completion: 31.00%
Remaining: 00:00:23
Total: 00:00:33
Expected Finish: 3:30:07PM 14-Nov-2017
Полезно для оценки завершения текущей симуляции, особенно той, которая может занимать часы или дни.
Ответ 2
Начиная с R2013b, вы можете использовать PARFEVAL
для асинхронной оценки своей функции и обновления отображения хода клиента. (Очевидно, что этот подход не так прост, как добавление материала в цикл PARFOR). Здесь приведен пример здесь.
Свойство Diary
Future
, возвращаемое PARFEVAL
, постоянно обновляется во время обработки, поэтому может быть полезно, если у вас есть небольшое количество больших задач.
Ответ 3
после изучения ответа @Edric Я обнаружил, что в документации Matlab есть пример, который точно реализует панель ожидания для цикла pareval. Проверьте help FetchNext
N = 100;
for idx = N:-1:1
% Compute the rank of N magic squares
F(idx) = parfeval(@rank, 1, magic(idx));
end
% Build a waitbar to track progress
h = waitbar(0, 'Waiting for FevalFutures to complete...');
results = zeros(1, N);
for idx = 1:N
[completedIdx, thisResult] = fetchNext(F);
% store the result
results(completedIdx) = thisResult;
% update waitbar
waitbar(idx/N, h, sprintf('Latest result: %d', thisResult));
end
% Clean up
delete(h)
Ответ 4
Начиная с R2017a, вы можете использовать parallel.pool.DataQueue
и afterEach
, чтобы реализовать waitbar
для parfor
, например:
if isempty(gcp('nocreate'))
parpool('local', 3);
end
dq = parallel.pool.DataQueue;
N = 10;
wb = waitbar(0, 'Please wait...');
% Use the waitbar UserData to track progress
wb.UserData = [0 N];
afterEach(dq, @(varargin) iIncrementWaitbar(wb));
afterEach(dq, @(idx) fprintf('Completed iteration: %d\n', idx));
parfor idx = 1:N
pause(rand());
send(dq, idx);
end
close(wb);
function iIncrementWaitbar(wb)
ud = wb.UserData;
ud(1) = ud(1) + 1;
waitbar(ud(1) / ud(2), wb);
wb.UserData = ud;
end