16

我在 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

4 回答 4

19

简单的进度条

类似于进度条之类的东西可以这样做......

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

用于估计运行模拟的完成情况,尤其是可能需要数小时或数天的模拟。

于 2014-12-09T12:29:08.707 回答
3

从 R2013b 开始,您可以使用PARFEVAL异步评估您的函数并让客户端显示进度更新。(显然,这种方法并不像在 PARFOR 循环中添加东西那么简单)。这里有一个例子。

返回的Diary属性在处理过程中不断更新,因此如果您有少量大型任务,这也可能很有用。FuturePARFEVAL

于 2014-05-07T11:49:20.207 回答
1

从 R2017a 开始,您可以使用parallel.pool.DataQueueandafterEach来实现waitbarfor 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
于 2017-11-01T09:20:21.173 回答
0

在探索了@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)
于 2017-10-27T11:52:59.697 回答