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)
Другие вопросы по тегам