Created
October 17, 2009 16:03
-
-
Save pmav/212380 to your computer and use it in GitHub Desktop.
Firefox web workers example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Firefox Web Workers Example v1.3.1 | |
* | |
* http://pmav.eu/stuff/javascript-webworkers | |
* | |
* This example uses N Web Workers for generating LCG pseudorandom numbers, each Worker starts a new stream. | |
* | |
* pmav, 2009-2010 | |
*/ | |
(function () { | |
MAIN = { | |
workersNumber : 0, // Total number of Web Workers (dynamic) | |
workers: [], // Web Workers reference | |
workersEnded : 0, // Number of terminated Web Workers | |
lcgNumber : 0, // # of LCG numbers to generate (dynamic) | |
lcgNumberPower : 0, // LCG numbers in powers of 2 | |
startTime : null, // Current test start time | |
results : null, // Current test results (for debugging) | |
testNumber : 1, // Current test number | |
benchmark : { // Benchmark stuff | |
mode : false, | |
currentTest : 0, | |
currentRun: 0, | |
runResults : [], | |
finalResults : [], | |
tests : [ | |
{ | |
workers: 1, | |
size: 27, // 2^27 | |
runs: 10 | |
}, | |
{ | |
workers: 2, | |
size: 27, | |
runs: 10 | |
}, | |
{ | |
workers: 4, | |
size: 27, | |
runs: 10 | |
}, | |
{ | |
workers: 8, | |
size: 27, | |
runs: 10 | |
} | |
] | |
}, | |
/** | |
* MAIN.run() | |
* | |
* Main code function (this will run only once per test) | |
*/ | |
run : function(workersNumber, lcgNumberPower) { | |
var i, t, interval, lcgNumberSeed; | |
this.workersEnded = 0; | |
this.workers = []; | |
this.results = []; | |
try { | |
// Get test values | |
if (typeof workersNumber === "undefined") { | |
this.workersNumber = parseInt(document.getElementById("webWorkersNumber").innerHTML, 10); | |
t = parseInt(document.getElementById("lcgNumber").innerHTML, 10); | |
this.lcgNumber = Math.pow(2, t); | |
this.lcgNumberPower = t; | |
} else { | |
this.workersNumber = workersNumber; | |
this.lcgNumber = Math.pow(2, lcgNumberPower); | |
this.lcgNumberPower = lcgNumberPower; | |
} | |
interval = Math.ceil(this.lcgNumber / this.workersNumber); | |
this.startTime = new Date(); | |
// Setup Workers | |
for (i = 0; i < this.workersNumber; i++) { | |
this.workers[i] = new Worker(WORKER.scriptFilename); | |
this.workers[i].onmessage = this.callback; | |
this.workers[i].onerror = this.error; | |
} | |
// Start workers | |
for (i = 0; i < this.workersNumber; i++) { | |
lcgNumberSeed = 1 + (interval * i); | |
if (lcgNumberSeed + interval > this.lcgNumber) { | |
interval = this.lcgNumber - (interval * i); | |
} | |
this.sendMessage(i, lcgNumberSeed, interval); | |
} | |
} catch (e) { | |
alert(e); | |
} | |
}, | |
/** | |
* MAIN.benchmark(); | |
* | |
* Run several tests and output some useful info | |
*/ | |
runBenchmark : function () { | |
if (this.benchmark.currentTest == 0) { | |
// Benchmark Start | |
this.benchmark.mode = true; | |
} | |
if (this.benchmark.currentRun < this.benchmark.tests[this.benchmark.currentTest].runs) { | |
// Still have some runs to do | |
this.run(this.benchmark.tests[this.benchmark.currentTest].workers, this.benchmark.tests[this.benchmark.currentTest].size); | |
this.benchmark.currentRun++; | |
} else { | |
// Next test? | |
this.benchmark.finalResults.push(UTIL.average(this.benchmark.runResults)); | |
this.benchmark.currentTest++; | |
// Reset Run | |
this.benchmark.currentRun = 0; | |
this.benchmark.runResults = []; | |
if (this.benchmark.currentTest < this.benchmark.tests.length) { | |
// Next test! | |
this.run(this.benchmark.tests[this.benchmark.currentTest].workers, this.benchmark.tests[this.benchmark.currentTest].size); | |
this.benchmark.currentRun++; | |
} else { | |
// Benchmark end | |
UTIL.log(""); | |
var i, s; | |
for (i = 0; i < this.benchmark.finalResults.length; i++) { | |
if (i == 0) { | |
s = "Standart Test, 100%"; | |
} else { | |
s = Math.round(((this.benchmark.finalResults[i] / this.benchmark.finalResults[0]) * 100)) + "%"; | |
} | |
UTIL.log("Benchmark Group #"+(i+1)+" average: "+this.benchmark.finalResults[i]+" ms, "+this.benchmark.tests[i].workers+ " Worker" + ( this.benchmark.tests[i].workers === 1 ? "" : "s" ) + " [" + s + "]"); | |
} | |
// Reset Test | |
this.benchmark.currentTest = 0; | |
this.benchmark.currentRun = 0; | |
this.benchmark.finalResults = []; | |
JQ.callback(); | |
} | |
} | |
}, | |
/** | |
* MAIN.sendMessage(int, int, int) | |
* | |
* Send a message to a Web Woker with id workerId | |
* | |
* workerId - Web Worker unique ID | |
* lcgNumberSeed - First LCG number (stream seed) | |
* iterations - # of LCG numbers that each thread will generate | |
*/ | |
sendMessage : function(workerId, lcgNumberSeed, iterations) { | |
try { | |
var data = {}; | |
data.workerId = workerId; | |
data.payload = {}; | |
data.payload.lcgNumberSeed = lcgNumberSeed; | |
data.payload.iterations = iterations; | |
this.workers[workerId].postMessage(JSON.stringify(data)); | |
} catch (e) { | |
throw e; | |
} | |
}, | |
/** | |
* MAIN.callback(object) | |
* | |
* Callback entry point (no context), this function executes in a single thread environment | |
*/ | |
callback : function(event) { | |
try { | |
var data = JSON.parse(event.data); | |
var workerId = data.workerId; | |
var payload = data.payload; | |
MAIN.workers[workerId].terminate(); | |
MAIN.workersEnded += 1; | |
MAIN.results[workerId] = payload.lastNumber; | |
if (MAIN.workersEnded === MAIN.workersNumber) { // All Web Workers are done | |
var time = ((new Date) - MAIN.startTime); | |
// Output run results | |
UTIL.log(MAIN.testNumber + ") " + MAIN.workersNumber + " Worker" + ( MAIN.workersNumber === 1 ? "" : "s" ) + ", Test Size: 2^"+MAIN.lcgNumberPower+" - "+ time + " ms"); | |
MAIN.testNumber += 1; | |
if (MAIN.benchmark.mode) { | |
MAIN.benchmark.runResults.push(time); | |
MAIN.runBenchmark(); | |
} else { | |
JQ.callback(); | |
} | |
} | |
} catch (e) { | |
alert("a: "+e); | |
} | |
}, | |
/** | |
* MAIN.error(object) | |
* | |
* Handles worker errors (in a amazing way...) | |
*/ | |
error : function(error) { | |
alert("Worker error: " + error.message); | |
} | |
} | |
/** | |
* WORKER | |
* | |
* Web Work related code | |
*/ | |
WORKER = { | |
scriptFilename : "assets/js/javascript-webworkers-v1.3.1.js", // Web Worker script | |
/** | |
* WORKER.run() | |
* | |
* Web Worker main code (thread dies after the execution of this code) | |
*/ | |
run : function() { | |
onmessage = function(event) { | |
var data = JSON.parse(event.data); | |
var workerId = data.workerId; | |
var payload = data.payload; | |
var n, i; | |
data.workerId = workerId; | |
data.payload = {}; | |
n = payload.lcgNumberSeed; | |
for (i = 0; i <= payload.iterations; i += 1) { | |
n = UTIL.linearCongruentialGenerator(n); | |
} | |
data.payload.lastNumber = n; // Only save the last number in the stream | |
postMessage(JSON.stringify(data)); // Callback to MAIN.callback() | |
return; | |
}; | |
}, | |
/** | |
* WORKER.isWorker() | |
* | |
* Check if active thread is a Web Worker | |
*/ | |
isWorker : function() { | |
if (typeof document === "undefined") { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
}; | |
/** | |
* UTIL | |
* | |
* Auxiliar code for this example | |
*/ | |
UTIL = { | |
a : 1103515245, // LCG a (glibc value) | |
c : 12345, // LCG c (glibc value) | |
m : 4294967296, // LCG M (glibc value) | |
/** | |
* linearCongruentialGenerator(int) | |
* | |
* Linear congruential generator: X(n+1) = ((a * X(n)) + c) % M | |
* More information: http://en.wikipedia.org/wiki/Linear_congruential_generator | |
*/ | |
linearCongruentialGenerator : function(n) { | |
return ((this.a * n) + this.c) % this.m; | |
}, | |
logDom : null, | |
log : function(loggingText) { | |
if (this.logDom === null) { | |
this.logDom = document.getElementById("log"); | |
} | |
this.logDom.innerHTML = loggingText + "<br>" + this.logDom.innerHTML; | |
}, | |
average : function(values) { | |
var i, sum = 0.0; | |
for (i = 0; i < values.length; i++) { | |
sum += values[i]; | |
} | |
return Math.floor(sum / values.length); | |
} | |
} | |
// Verifies if is a Web Worker thread that is executing at the moment | |
if (WORKER.isWorker()) { | |
WORKER.run(); | |
} | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment