/**
* Firefox Web Workers Example v1.4.0
*
* http://pmav.eu/stuff/javascript-webworkers
*
* This example calculates a^b % c using N Web Wokers.
*
* pmav, 2009-2010
*/
(function () {
MAIN = {
workersNumber : 0, // Total number of Web Workers (dynamic).
workers: [], // Web Workers reference.
workersEnded : 0, // Number of terminated Web Workers.
base : null,
power : null,
mod : null,
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,
runs: 10,
data : {
base : 2,
power : 2048000000, // 2 048 000 000
mod : 97777
}
},
{
workers: 2,
runs: 10,
data : {
base : 2,
power : 2048000000,
mod : 97777
}
},
{
workers: 4,
runs: 10,
data : {
base : 2,
power : 2048000000,
mod : 97777
}
},
{
workers: 8,
runs: 10,
data : {
base : 2,
power : 2048000000,
mod : 97777
}
},
{
workers: 16,
runs: 10,
data : {
base : 2,
power : 2048000000,
mod : 97777
}
}
]
},
/**
* MAIN.run(int, int, int, int)
*
* Main code function (this will run only once per test).
*/
run : function(workersNumber, base, power, mod) {
var i, powers;
// Initial setup.
this.workersEnded = 0;
this.workers = [];
this.results = [];
try {
this.workersNumber = workersNumber;
this.base = base;
this.power = power;
this.mod = mod;
// Divide work by Workers.
powers = UTIL.powerDivision(power, workersNumber);
// Setup Workers.
for (i = 0; i < workersNumber; i++) {
this.workers[i] = new Worker(WORKER.scriptFilename);
this.workers[i].onmessage = this.callback;
this.workers[i].onerror = this.error;
}
this.startTime = new Date();
// Start workers.
for (i = 0; i < workersNumber; i++) {
this.sendMessage(i, base, powers[i], mod);
}
} catch (e) {
alert(e);
}
},
/**
* MAIN.benchmark();
*
* Run several tests and output some useful info.
*/
runBenchmark : function () {
var test;
if (this.benchmark.currentTest == 0) {
// Benchmark start.
this.benchmark.mode = true;
}
while (this.benchmark.tests[this.benchmark.currentTest].runs == 0) {
// Skip current test.
this.benchmark.currentTest++;
}
if (this.benchmark.currentRun < this.benchmark.tests[this.benchmark.currentTest].runs) {
// Still have some runs to do.
test = this.benchmark.tests[this.benchmark.currentTest];
this.run(test.workers, test.data.base, test.data.power, test.data.mod);
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!
test = this.benchmark.tests[this.benchmark.currentTest];
this.run(test.workers, test.data.base, test.data.power, test.data.mod);
this.benchmark.currentRun++;
} else {
// Benchmark end.
UTIL.logBenchmark("");
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.logBenchmark("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 = [];
this.benchmark.mode = false;
JQ.callback();
}
}
},
/**
* MAIN.sendMessage(int, int, int, int)
*
* Send a message to a Web Woker with id workerId.
*
* workerId - Web Worker unique ID.
* base ^ power % mod
*/
sendMessage : function(workerId, base, power, mod) {
try {
var data = {};
data.workerId = workerId;
data.payload = {};
data.payload.base = base;
data.payload.power = power;
data.payload.mod = mod;
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 i, result, data = JSON.parse(event.data);
var workerId = data.workerId;
var payload = data.payload;
MAIN.workers[workerId].terminate();
MAIN.workersEnded += 1;
MAIN.results[workerId] = payload.result;
if (MAIN.workersEnded === MAIN.workersNumber) { // All Web Workers are done.
// Merge results.
result = 1;
for (i = 0; i < MAIN.results.length; i++) {
result = (result * MAIN.results[i]) % MAIN.mod;
}
var time = ((new Date) - MAIN.startTime);
// Output run results.
var output = MAIN.testNumber + ") " + MAIN.workersNumber + " Worker" + ( MAIN.workersNumber === 1 ? "" : "s" ) + ", Test: "+MAIN.base+"^"+MAIN.power+" mod "+MAIN.mod+" = "+result+", "+ time + " ms";
if (MAIN.benchmark.mode) {
UTIL.logBenchmark(output);
} else {
UTIL.log(output);
}
MAIN.testNumber += 1;
if (MAIN.benchmark.mode) {
MAIN.benchmark.runResults.push(time);
MAIN.runBenchmark();
} else {
JQ.callback();
}
}
} catch (e) {
alert(e);
}
},
/**
* MAIN.error(object)
*
* Handles worker errors (in a amazing way...).
*/
error : function(error) {
alert("Worker error: " + error);
}
}
/**
* WORKER
*
* Web Work related code.
*/
WORKER = {
scriptFilename : "assets/js/javascript-webworkers-v1.4.0.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 base, power, mod;
data.workerId = workerId;
data.payload = {};
base = payload.base;
power = payload.power;
mod = payload.mod;
data.payload.result = UTIL.power(base, power, mod);
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 = {
/**
* Return base^power % mod.
*/
power : function(base, power, mod) {
if (power == 0) {
return 1;
}
var i = 0, total = base;
power--;
for (i = 0; i < power; i++) {
total = (total * base) % mod;
}
return total;
},
/**
* Return p splited into n slots.
* Example:
* p = 13
* n = 3
* Return [5, 4, 4]
*/
powerDivision : function(p, n) {
var e = 0, i = 0, d = 0;
var a = [];
e = Math.floor(p/n);
for (i = 0 ; i < n; i++) {
a[i] = e;
}
if ((e * n) < p) {
d = p - (e * n);
for (i = 0 ; i < d; i++) {
a[i] += 1;
}
}
return a;
},
logDom : null,
logBenchmarkDom : null,
log : function(loggingText) {
if (this.logDom === null) {
this.logDom = document.getElementById("log");
}
this.logDom.innerHTML = loggingText + "
" + this.logDom.innerHTML;
},
logBenchmark : function(loggingText) {
if (this.logBenchmarkDom === null) {
this.logBenchmarkDom = document.getElementById("benchmark-log");
}
this.logBenchmarkDom.innerHTML = loggingText + "
" + this.logBenchmarkDom.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 it is a Web Worker thread that is executing at the moment.
if (WORKER.isWorker()) {
WORKER.run();
}
}());