エラトステネスのふるい 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起動や通信のコストが大きく、イベント処理などの小さな処理の並列化には向かないという感じ