我在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 progress打印机"比如this one。但是,所有这些都会打印整个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%
即显示同一行的所有进度。要做到这一点,你需要跟踪每个模拟要在线上写入和写入的位置,而不删除其他进程。我无法弄清楚如何做到这一点,这可能吗?
如果我的问题有其他解决办法(显示每次迭代的进度),我还没有想过,我很高兴听到它。
因为这是我第一次在这里问一个问题,所以很可能有一些我错过的东西;如果是这样,请随时在下面发表评论。
收到this answer之后,我认为我应该分享如何使用它来解决我的问题,因为我没有像答案中那样使用它,以防其他人遇到同样的问题。< / p>
这是一个小型测试程序,其结构与我的程序基本相同,使用了答案中提到的进度条(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
答案 0 :(得分:16)
像进度条这样的东西可以像这样做......
在parfor
循环之前:
fprintf('Progress:\n');
fprintf(['\n' repmat('.',1,m) '\n\n']);
在循环中:
fprintf('\b|\n');
这里我们有m
是迭代总数,.
显示迭代总数,|
显示迭代完成次数。 \n
确保字符在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
(退格)字符,因此命令窗口不会充满文本。虽然不是严格意义上的进步' bar ',但它可能更具信息性。
函数文件标题中的第三个示例
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
用于估计正在运行的模拟的完成,尤其是可能需要数小时或数天的模拟。
答案 1 :(得分:3)
从R2013b开始,您可以使用PARFEVAL
异步评估您的功能,并让客户端显示进度更新。 (显然,这种方法并不像向PARFOR循环添加内容那么简单)。有一个例子here。
Diary
返回的Future
属性PARFEVAL
在处理过程中会不断更新,因此如果您有少量大型任务,这也可能很有用。
答案 2 :(得分:1)
从R2017a开始,您可以使用parallel.pool.DataQueue
和afterEach
为parfor
实施waitbar
,如下所示:
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
答案 3 :(得分:0)
在探索@Edric回答后,我发现Matlab文档中有一个示例,它准确地为pareval循环实现了一个等待条。检查public static void Run([TimerTrigger("*/5 * * * * *")]TimerInfo myTimer, TraceWriter log)
help FetchNext