Matlab シミュレーションの多重実行と自動記録 (教員のための Mac Tips:11)

背景

Matlab での開発環境がかなり整ってきました。自作の画像クラスはグレースケールだけだなくカラーにも対応し、 OpenEXR の浮動小数点表現、色空間変換、トーンマッピングなどいろいろと取り込んでかなり膨らんできました。クラスのテストについては、MATLAB と自動テスト環境 (教員のための Mac Tips:10) で設定した fswatch によるフォルダ内ファイルの変更検知機能により、自動テストができています。テストや実装のファイルを保存するだけで、fswatch の下で Matlab が起動し、テスト結果がターミナルに表示されます。実装が終了しテストが全て通過したら、fswatch を止め、作成したテスト及び実装を git リポジトリにコミットします。この時の親リポジトリに、Jenkins 導入と MATLAB の自動テスト (教員のための Mac Tips:9)で記述したフックスクリプトを設定しているので、Jenkins ビルドが呼ばれます。Jenkins ビルドでは、リポジトリの取得後に Matlab を起動してテスト結果を記録します。

上記のクラスメソッドができた段階で、それらを利用したシミレーションプログラムを作成し GUI Matlab で実行していました。最近は、「Matlab でシミュレーション→結果をタブ区切りテキストで画面表示→結果文字列をコピーしてNumbersの表に貼り付け→シミュレーション条件やグラフを一つのシートにまとめて報告書として整形→ボスに報告」という一連の流れ作業ができています。ここで、一番のネックなのが一番最初のシミュレーション部です。私の場合、画像依存性があるシミュレーションが多く、画像を変えて何度も行うことが多いです。そのため、一つの画像でうまく動いたプログラムを入力を変えて何度も回すことがあります。普通に書いた Matlab スクリプトはシングルタスクとなるため、最近のCPU の持つ複数コアは有効に使われず、CPU は遊びまくっています。シングルタスクであればターボブースト機能でクロックが上がりますが、やはり複数 CPU をこき使ってあげたくなるのが心情です。対策は幾つかあります。

複数の Version の Matlab を同時に動かす
簡単ですが、メモリも食いますし画面切替も面倒です。並列度にも上限があります。
parfor を使う
プログラムを parfor 用に直す必要があります。私の場合画面に結果を出しているので、並列で出力されると出力の加工が面倒です。
ターミナルを複数開き、コマンドライン Matlab をそれぞれで起動する
一番解決策に近いですが、画面がターミナルで溢れるのが面倒なのとジョブ管理をしたくありません。Matlab の画面だけで解決したいです。

自動化が面倒になったら Jenkins ということで、一連の作業を Jenkins 対応にしました。この記事はその作業のまとめです。

Matlab 側の設定

前回の記事で書き忘れましたが、Matlab の出力はデフォルトで ShiftJIS になっています。このままだと Jenkins で文字化けするので、setup.m でデフォルトの文字コードUTF-8 にしておきます。また、私はデフォルトシェルを fish にしているのですが、外部呼び出し時にいろいろとトラブルので SHELL 変数を /bin/sh に設定しています。setup.m の内容は以下のようになっています。

setenv('SHELL', '/bin/sh');
feature('DefaultCharacterSet', 'UTF-8');
feature('DefaultCharacterSet');

matlab に sim.m というファイルを設定します。これは、Jenkins を外部から呼び出すようにするものです。

function out = sim(func, params)
    display(sprintf('executing: %s(%s) in background', func, params));
    system(sprintf('/usr/local/bin/wget --auth-no-challenge --http-user=hkob --http-password=設定されたAPIパスワード http://localhost:8080/job/ResearchSimulations/buildWithParameters\\?token=設定したトークン\\&function=%s\\&parameters=%s -O /dev/null > /dev/null 2>/dev/null', func, params));
end

例えば、

h20140421_2(1);

のようにしてうまく動いたスクリプトがあったとき、

sim('h20140421_2', '1');

のようにするとこのプログラムを Jenkins 上で実行できるようになります。

Jenkin 上の設定

Jenkins の設定は、以前の記事で書いたとおりです。ただし、最大同時実行数を 6 まで上げています。必要な設定は以下の通りです。

ビルドのパラメータ化
function と parameters という二つの文字列を追加します。
ビルドを並列実行
チェックを入れておきます。
リモートからビルド
チェックを入れて、トークンを設定します

ビルドはシェルを実行とし、以下のスクリプトを記述します。

#!/bin/sh

COMMAND=$function\($parameters\)
echo $COMMAND
/Applications/MATLAB_R2014a.app/bin/matlab -nojvm -nodisplay -nosplash -r "cd /Users/hkob/Documents/MATLAB; global build_number; build_number = $BUILD_NUMBER; $COMMAND; exit(0)"

ここで、build_number という変数をグローバル変数として定義しています。外部ファイルを出力すべき時などは、この build_number 変数を使ってファイルがぶつからないようにしておきます。

まとめ

試しに sim 関数を 7 個連続して呼びましたが、Jenkins 上で 6 個まで並列実行され、残りの一つは待ち行列にちゃんと溜まりました。CPU も i7 の 8 スレッドを全部有効利用され、シミュレーション時間も短縮しました。結果がすべて Jenkins のコンソールに残るので、Numbers への張り込みも簡単でした。シミュレーション効率がさらにあがりそうです。今は sim を沢山書きましたが、パラメータを複数列記して sim を呼び出すような簡易スクリプトを追加してもいいですね。

written by iHatenaSync