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

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

![waitbar.png](https://qiita-image-store.s3.amazonaws.com/0/109898/b470ed88-19ae-5dbd-90d4-8d5c20c54685.png)


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

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

くらいであり、それ以外は代用として、シンプルな`fprintf()`を使うのがよい。見た目は格好良くないが、繰り返しの中でどこにいるか知りたいだけならこちらのほうが余程早いのだ。

下のコードでは `fprintf()` と `waitbar()` の比較を行ってみた。

```matlab
% 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()`を賢く使うためには、ループ毎に更新させるのではなく、下のように、何回かの繰り返し毎にしか更新しないよう工夫したほうが良さそうだ。

```matlab

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
```