Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

MATLABのwaitbar()はできるだけ避けたほうが良い理由

More than 3 years have passed since last update.

気が短い人なら、MATLABで長たらしい for loop を実行中に、waitbar() を設置したくなるだろう。しかし、waitbar()はできうる限り避けたほうが良い。どうしても使う場合はよく考えて設置する必要あり。MATLABの描画機能は遅いと知られている(と思う)。waitbar()はもろにその影響を受けてしまっている。

waitbar.png

waitbar()を使っても良い場合はたとえば、

  1. 込み入った進捗状況メッセージがどうしても必要な場合,
  2. Cancelボタンを設置する必要がある場合,
  3. forループの繰り返し回数が比較的小さい場合,
  4. 一回のループの処理がかなり長い(数分以上?)場合

くらいであり、それ以外は代用として、シンプルな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
kouichi-c-nakamura
中村公一はイギリス在住の神経科学者。 2000年 京都大学理学部 卒業 2000年 同・大学院 理学研究科 理学修士 2006年 同・大学院 医学研究科 高次脳形態学、医学博士 2013年-2016年 京都大学 大学院医学研究科高次脳形態学助教 2017年現在、英国・オックスフォード大学のポスドク(Magill lab)
http://www.mrcbndu.ox.ac.uk/people/dr-kouichi-c-nakamura
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away