エラトステネスのふるい on Google Gears WorkerPool

エラトステネスのふるい on JavaScript-1.7/1.8 - ラシウラGoogle Gearsのworkerpool上に実装してみました。workerには制限がいくつかあり、その対策は少し面倒でした。

  • alert等は使えない
  • createWorkerできない

これらを実現するため、親workerPool(ID=0で呼び出せる)に["p", message]や["new"]という感じでイベントを送ったり、新たなworkerのIDを送り返したりしてます。後者はnewを送ってからIDを受け取るまで間があるので、その間にもメッセージが飛んでくるので、新workerのIDを受け取るまでメッセージを溜め込んだりしてます。

gears_init.jsはGoogleの例のではなくGoogle Chromeを入れたついでにGoogle Gearsを試してみた - ラシウラのものです。

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="gears_init.js"></script>
<script>//<![CDATA[
window.onerror = function (err) { alert(err); }

var workerPool = null;
var rootId;

function doInit() {
  var factory = initFactory();
  workerPool = factory.create('beta.workerpool');
}
function output(m) {
  document.getElementById("output").innerHTML += m + "<br />";
}

function doCreateWorker() {
  workerPool.onmessage = function (a, b, message) {
    switch (message.body[0]) {
    case "new":
      var sieveId = workerPool.createWorkerFromUrl('sieve.js');
      workerPool.sendMessage(["created", sieveId], message.sender);
      output("created id: " + sieveId);
      break;
    case "print":
      output("prime: " + message.body[1]);
      break;
    case "p":
      output("[" + message.body[1] + "]");
      break;
    }
  };
  rootId = workerPool.createWorkerFromUrl("sieve.js");
  output("created root id: " + rootId);
}
function doSendWorker() {
  for (var n = 2; n < 100; n += 1) {
    workerPool.sendMessage(["pass", n], rootId);
  }
  output("[sent 2..100]");
}
//]]></script>
</head>
<body onload="doInit()">
<input type="button" value="create" onclick="doCreateWorker()"/>
<input type="button" value="send" onclick="doSendWorker()"/>
<div id="output"></div>
</body>
</html>


sieve.js

var wp = google.gears.workerPool;
wp.onmessage = (function () {
  //wp.sendMessage(["p", "new worker"], parent);
  var parent = 0; // workers cannot call wp.createWorker, alert, etc...
  var next = null;
  var prime = null;
  var pooled = [];
  var procSieve = function (n) {
    if (n % prime !== 0) wp.sendMessage(["pass", n], next);
  };
  return function (a, b, message) {
    //wp.sendMessage(["p", "receive next: " + next + " prime: " + prime], parent);
    switch (message.body[0]) {
    case "created":
      next = message.body[1];
      for (var i = 0; i < pooled.length; i += 1) {
        procSieve(pooled[i]);
      }
      break;
    case "pass":
      var n = message.body[1];
      if (prime === null) {
        prime = n;
        wp.sendMessage(["print", prime], parent);
        wp.sendMessage(["new"], parent);
      } else if (next === null) {
        pooled.push(n);
      } else {
        procSieve(n);
      }
      break;
    }
  };
})();

google.gears.workerPoolというのは決めうちのようです。

別段closureを使わなくても、トップレベルの変数やgoogle...以下にセットしたりすることでも、workerのための状態を保持できます。

ちなみにChrome上では処理速度は泣けるほど遅かったです*1><

*1:Worker起動や通信のコストが大きく、イベント処理などの小さな処理の並列化には向かないという感じ