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 итераций в parfor
цикл, но каждая итерация занимает около часа, этот подход не очень полезен для меня.
Идеальным решением для меня было бы то, что выглядит следующим образом
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
4 ответа
Простой индикатор прогресса
Нечто похожее на индикатор выполнения можно сделать похожим на это...
Перед 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
"S.
Предполагая, что вы правильно добавили вышеприведенное в путь 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
(возврат), чтобы командное окно не было заполнено текстом. Хотя это не совсем " индикатор прогресса", но, возможно, он более информативен.
Третий пример в заголовке файла функции,
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
Полезно для оценки завершения беговой симуляции, особенно той, которая может занять часы или дни.
Начиная с R2013b, вы можете использовать PARFEVAL
для асинхронной оценки вашей функции и отображения клиентом обновлений прогресса. (Очевидно, что этот подход не так прост, как добавление материала в ваш цикл PARFOR). Здесь есть пример.
Diary
собственность Future
вернулся PARFEVAL
обновляется непрерывно во время обработки, что также может быть полезно, если у вас небольшое количество больших задач.
Начиная с 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's 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
Изучив ответ @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)