I have observed an odd behavior of web workers. Assume worker.js to containt the code below
onmessage =
function(event)
{
var x = event.data;
var c = 0;
for(var i = 0; i < x; i++) c = c + Math.random();
postMessage(c);
}
I create and worker, post "100000000" to it, to run it for 100 millions loops. It takes around 900 milliseconds.
Now, consider the same code, without an onmessage event. It will run right away after being created, without having to trigger it through postMessage.
var x = 100000000;
var c = 0;
for(var i = 0; i < x; i++) c = c + Math.random();
postMessage(c);
Same 100 millions loops, but this this time it takes on average 2200 milliseconds. More than double.
Is there any explanation for this performance gap? A compilation issue perhaps?
Update, as requested, this is how performance is measured:
var worker = new Worker('worker.js');
var time = performance.now();
worker.onmessage =
function(event)
{
time = performance.now() - time;
}
V8 developer here.
What's happening here is that accessing (in particular: writing to) global variables is slower than accessing function-local variables, mostly because optimized code can keep the latter in registers, but not the former.
This is not a bug, not related to workers, and everything is getting optimized.
Luckily, real-world code almost never has hot code in the top level, so this is mostly just another microbenchmarking artifact.
Reduced example:
(function fast() {
var x = 100000000;
const t1 = performance.now();
for (var i = 0; i < x; i++) {}; // i is local
const t2 = performance.now();
console.log("fast", t2 - t1);
})();
(function slow() {
var x = 100000000;
const t1 = performance.now();
for (i = 0; i < x; i++) {}; // oops, forgot `var`, i is global
const t2 = performance.now();
console.log("slow", t2 - t1);
})();
Putting code into the toplevel scope is another variant of function slow
, because all variables are global variables there.