SSK's Memo


We Love Multi Task !!

About This Contents

JavaScriptのWeb Workersと逐次処理の性能比較。

Web Workersによるマルチタスクを実装。

Coding

Web Workersによる並列処理の注意点

  • 各タスクはバックグラウンド実行
  • タスク間でメモリは共有されない
  • タスク間のデータの受け渡しは2種類
    • 複製:
      各タスクのメモリ空間にデータを複製する
    • 転送:
      任意のタスクに任意のArrayBufferを送り、送り元のArrayはNullとなる
      「転送」は「複製」の10~100倍高速(一部ブラウザ非対応)
  • 各タスクは非同期処理

以下、内積のサンプルコード

▼ main.js(呼び出し元)

function dot(n,a,b) {                                 //内積計算
   var ncore = navigator.hardwareConcurrency;         //CPUの論理スレッド数の所得
   var offset = Math.ceil(n/ncore);
   var param = new Object();                          //各タスクに転送するパラメータ
   param.n = n;                                       //  パラメータ:内積用ベクトル全体の配列長
   param.ncore = ncore;                               //  パラメータ:タスク数
   param.offset = offset;                             //  パラメータ:タスク毎のベクトルの配列長
   calcWorker = new Array(ncore);                     //処理の分割数だけWorkerオブジェクトが必要(配列化可能)
   var r = new Array(ncore);
   var synCnt = 0;                                    //同期用カウンタ
   var ans = 0.0;                                     //最終的な答え用変数

   for( var i=0; i<(ncore)|0; i=(i+1)|0 ) {           //各タスクのパラメータの決定とデータ転送
      var is = offset * i;
      var ie = offset + is;
      if( i == ncore-1 ) { ie = n; }
      param.vecA = a.slice(is,ie);                    //  パラメータ:分割配列
      param.vecB = b.slice(is,ie);                    //    ※転送後、vecA,vecBはNullになるため注意
      param.index = i;                                //  パラメータ:タスクID
      calcWorker[i] = new Worker('dot_pt.js');        //タスク毎にWorkerを割り当てる
      
      //Workerをバックグラウンドへ転送・実行(一部ブラウザは配列を転送でなく複製するためボトルネックとなる)
      calcWorker[i].postMessage(param,[param.vecA.buffer, param.vecB.buffer]);
      
      calcWorker[i].onmessage = function(e) {         //バックグラウンドのWorkerから計算結果が戻ってきたときの処理
         r[e.data.index] = e.data.ans;
         synCnt++;
         if( synCnt == ncore ) {                      //すべてのタスクが完了したときの処理
            for( var j=0; j<(ncore)|0; j=(j+1)|0 ) {
               ans += r[j];                           //各タスクの答えをansに合算し、内積完了
            }
            return ans;
         }
      }
   }
}

▼ dot_pt.js(呼び出し先:バックグラウンドの処理)

onmessage = function(e) {                             //Workerの処理
   var ans = 0.0;                                     //Workerで求めた答えを記録する変数
   var index = e.data[0].index;
   var a = e.data[0].vecA;
   var b = e.data[0].vecB;
   var ie = e.data[0].offset;
   if( index == e.data[0].ncore-1 ) {
      ie = e.data[0].n - index * ie;
   }

   for( var i=0; i<(ie)|0; i=(i+1)|0 ) {
      ans = ans + a[i] * b[i];                        //分割されたベクトルの内積の計算
   }

   var data = new Object();                           //返信用オブジェクト
   data.index = index;
   data.ans = ans;
   postMessage(data);                                 //求めた答えを呼び出し元へ複製
}

Calculation of Dot()

Web Workersで内積を実装し、速度および性能を比較。[Exec]から実行。

転送非対応のブラウザは複製を行うため、処理時間中の転送時間がボトルネックとなり並列化効率が低い。

並列処理逐次処理
配列長処理時間 [s]転送時間 [s]性能 [GFLOPS]処理時間 [s]性能 [GFLOPS]並列/逐次