Web Workersによるマルチスレッド化で並列処理
Web Workersの試験では、Webブラウザで実行するJavaScript上でメイン(UI)スレッドとは別のワーカースレッドを作成してみました。Web Workersを使うことで、時間のかかる処理をWebブラウザ本体の処理をとめることなく実行できることを確認しましたが、このWeb Workerのスレッドは複数作成することができます。
JavaScriptでも、Web Workersを使うことで複数のスレッドで処理する「マルチスレッド(並列)処理」ができるわけですね。
今回は、前回のマンデルブロ集合描画処理をマルチスレッドで並列処理してみましょう。処理の開始時に、複数のWeb Workersを作成して、それぞれに「スレッド分割数(dibvT)」と「スレッド番号(indexT)」のパラメータを与えます。
// Web Workersオブジェクトをスレッド数の分だけ作成
for (i = 0;i < num;i++) {
param.divT = num;
param.indexT = i;
worker = new Worker('webworkers_mt1.js');
worker.addEventListener('message', function(e) { webworkers_mm_onWorkerMessage(e); }, false);
worker.postMessage(param);
}
描画処理では
for (i = 0;i < height / divT;i++) { ii = i * divT + indexT; ・・ii行に対する処理
という風に、行(Y座標)ごとに処理を分割してみました。スレッド数が2なら、Y座標が0,2,4…(偶数)の行をスレッド0で、1,3,5…(奇数)の行をスレッド1で描画するわけですね。
各スレッドでは、処理が終わるとメイン側にスレッド番号とともに結果をメッセージ送信します。メイン側では、受け取ったスレッド番号を元に処理結果がどの部分に対応するのか判別してImageDataにコピーするようにしました。
function webworkers_mm_onWorkerMessage(e) {
・・・
// Web Workersのスレッドから受け取ったデータをImageDataにコピー
for (i = 0;i < webworkers_mm_imageHeight / e.data.divT;i++) {
// 行先頭のオフセットを算出
var index = ((i * e.data.divT + e.data.indexT) * webworkers_mm_imageWidth) * 4;
// 行にピクセルデータを転送
for (j = 0;j < webworkers_mm_imageWidth * 4;j++) {
webworkers_mm_imageData.data[index + j] = e.data.buf[i * webworkers_mm_imageWidth * 4 + j];
}
}
・・・
}
描画を行うスレッド数は、メニューで1-4の間で指定する形にしてみました。まず、1シングルスレッドでどの程度時間がかかるか確認した後、スレッド数を増やしてマルチスレッド化の効果を確認してみましょう。
私の環境では、パソコン(Core2 Duo/Windows Vista32ビット版 + Firefox 25)の場合
1スレッド | 4071ms |
---|---|
2スレッド | 2388ms |
3スレッド | 2262ms |
4スレッド | 2316ms |
といった結果でした。また、Nexus 7(Android 4.2/ Firefox)では
1スレッド | 13558ms |
---|---|
2スレッド | 7003ms |
3スレッド | 4973ms |
4スレッド | 5035ms |
となりました。Nexus 7のCPUはTegra 3(4コアCPU)ですから、Web Workersによるマルチスレッド化が大きな効果を発揮するようですね。
Web Workersの実装状況は、パソコンはほぼすべてのWebブラウザが対応(ただしIE9が未対応なのが不安点か)、iOSもiOS5以降ですから現状ではほとんどの端末が対応しているといえるでしょう。
Androidの場合は以前からFirefoxがWeb Workersに対応しているのですが、標準ブラウザは4.4以降(と2.1)の対応となるようです。Android向けのWebアプリでは、しばらくWeb Workersを使いにくい状況が続くかもしれません。
もっとも、標準ブラウザに組み込まれた以上は、今後の普及が約束されたようなもの。中長期的にはAndroidでも「当たり前」のものになるのでしょう。
ここ数年、JavaScriptの最適化技術が急速に進歩し、Webブラウザ上のJavaScriptの実行速度は大幅に高速化されました。ただ、JavaScriptのコード自体はシングルスレッドでの実行が前提であったために、特にモバイルで加速するマルチコア化の恩恵を受けにくかった面もあります。
Web Workersは、JavaScriptのコード実行の流れ自体をマルチスレッド化するものですから、ようやくAPIレベルでマルチコア時代に対応できるようになった、といえるかもしれません。今後は、WebアプリでもマルチコアCPUによる並列処理で複雑な処理も効率的にこなせるようになっていきそうですね。