気が短い人なら、MATLABで長たらしい for loop を実行中に、waitbar()
を設置したくなるだろう。しかし、waitbar()
はできうる限り避けたほうが良い。どうしても使う場合はよく考えて設置する必要あり。MATLABの描画機能は遅いと知られている(と思う)。waitbar()
はもろにその影響を受けてしまっている。
waitbar()
を使っても良い場合はたとえば、
- 込み入った進捗状況メッセージがどうしても必要な場合,
- Cancelボタンを設置する必要がある場合,
-
for
ループの繰り返し回数が比較的小さい場合, - 一回のループの処理がかなり長い(数分以上?)場合
くらいであり、それ以外は代用として、シンプルなfprintf()
を使うのがよい。見た目は格好良くないが、繰り返しの中でどこにいるか知りたいだけならこちらのほうが余程早いのだ。
下のコードでは fprintf()
と waitbar()
の比較を行ってみた。
% waitbarVAfprintf
trials = 10;
elapsed1 = zeros(10,1);
for j = 1:10
clear n
tic
n = 1000;
fprintf('%d to go\n',n)
for i = 1:n
fprintf('*')
end
fprintf('\n')
elapsed1(j) = toc;
end
elapsed2 = zeros(10,1);
for j = 1:10
clear n
tic
n = 1000;
for i = 1:n
if round(i/n*100) > round((i-1)/n*100)
fprintf('*')
end
end
fprintf('\n')
elapsed2(j) = toc;
end
elapsed3 = zeros(10,1);
for j = 1:10
clear n wbmsg wb
tic
n = 1000;
for i = 1:n
wbmsg = sprintf('In progress: %.1f%%', i/n*100);
if ~exist('wb', 'var')
wb = waitbar((i-1)/n, wbmsg);
else
waitbar((i-1)/n, wb, wbmsg);
end
end
delete(wb) % cannot use 'close'!!!
elapsed3(j) = toc;
end
elapsed4 = zeros(10,1);
for j = 1:10
clear n wbmsg wb
tic
n = 1000;
for i = 1:n
if round(i/n*100) > round((i-1)/n*100)
wbmsg = sprintf('In progress: %.1f%%', i/n*100);
if ~exist('wb', 'var')
wb = waitbar((i-1)/n, wbmsg);
else
waitbar((i-1)/n, wb, wbmsg);
end
end
end
delete(wb) % cannot use 'close'!!!
elapsed4(j) = toc;
end
%%
fprintf('1. fprintf: %f %s %f msec per cycle\n',...
mean(elapsed1),char(177),std(elapsed1));
fprintf('2. fprintf for each %%: %f %s %f msec per cycle\n',...
mean(elapsed2),char(177),std(elapsed2));
fprintf('3. waitbar: %f %s %f msec per cycle\n',...
mean(elapsed3),char(177),std(elapsed3));
fprintf('4. waitbar for each %%: %f %s %f msec per cycle\n',...
mean(elapsed4),char(177),std(elapsed4));
fprintf('fprintf is %.1f-fold faster than waitbar updating at every cycle\n',...
mean(elapsed3./elapsed1));
fprintf('fprintf for each %% is %.1f-fold faster than waitbar for each %%\n',...
mean(elapsed4./elapsed2));
fprintf('fprintf for each %% is %.1f-fold faster than frpintf updating at every cycle\n',...
mean(elapsed1./elapsed2));
fprintf('waitbar for each %% is %.1f-fold faster than waitbar updating at every cycle\n',...
mean(elapsed3./elapsed4));
fprintf('fprintf for each %% is %.1f-fold faster than waitbar updating at every cycle\n',...
mean(elapsed3./elapsed2));
結果はこんな感じである。
fprintf is 360.9-fold faster than waitbar updating at every cycle
fprintf for each % is 510.3-fold faster than waitbar for each %
fprintf for each % is 10.7-fold faster than frpintf updating at every cycle
waitbar for each % is 7.5-fold faster than waitbar updating at every cycle
fprintf for each % is 3829.7-fold faster than waitbar updating at every cycle
見給え、 fprintf()
がループ毎に *
をコマンドウィンドウに出力する(1000個の*
が並ぶ)場合、waitbar()
よりも実に361倍も高速である。
fprintf()
に、1%の進捗毎に*
を1個出力するようにさせた場合(100個の*
印が並ぶだけ)、同様に1%の進捗毎に更新する waitbar()
よりも510倍も高速である。
fprintf()
に、1%の進捗毎に*
を1個出力するようにさせた場合(100個の*
印が並ぶだけ)、ループ毎に更新する waitbar()
よりも3,830倍も高速である!!!
結論: waitbar()
が驚くほど遅いことは知っておくべきだ。waitbar()
を賢く使うためには、ループ毎に更新させるのではなく、下のように、何回かの繰り返し毎にしか更新しないよう工夫したほうが良さそうだ。
n = 1000;
for i = 1:n
if round(i/n*100) > round((i-1)/n*100)
wbmsg = sprintf('In progress: %.1f%%', i/n*100);
if ~exist('wb', 'var')
wb = waitbar((i-1)/n, wbmsg);
else
waitbar((i-1)/n, wb, wbmsg);
end
end
end