The following code performs a silent logical error:
const arr = [];
class Point{
constructor(){
this.x = Math.random() * 1000000;
this.y = Math.random() * 1000000;
}
}
console.time('foo');
let avg = 0;
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
// shouldn't this double the avg ?
for(let i = 0; i < 114000000; i++ ){
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
console.timeEnd('foo');
CodePen -
Possible behaviour(s):
The variable
avg
after the second for loop should be doubled and The length of array should be 114 million.I should get a memory error.
Output when run as a script:
avg
Does not change after the second for loop.- Length of the array is not 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
The following code performs a silent logical error:
const arr = [];
class Point{
constructor(){
this.x = Math.random() * 1000000;
this.y = Math.random() * 1000000;
}
}
console.time('foo');
let avg = 0;
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
// shouldn't this double the avg ?
for(let i = 0; i < 114000000; i++ ){
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
console.timeEnd('foo');
CodePen - http://codepen.io/darkyen/pen/yOPMZg?editors=0010
Possible behaviour(s):
The variable
avg
after the second for loop should be doubled and The length of array should be 114 million.I should get a memory error.
Output when run as a script:
avg
Does not change after the second for loop.- Length of the array is not 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
2 Answers
Reset to default 33When you write code in Codepen - they actually don't execute it as-is but rather first apply some transformations to it.
They parse it into an abstract syntax tree, find loops and insert instructions explicitly to stop executing the loop if too much time has passed.
When you do:
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
Your code runs as:
for (var i = 0; i < 114000000; i++) {
if (window.CP.shouldStopExecution(1)) { // <- injected by Codepen!!!
break;
}
arr.push(new Point());
avg += arr[i].x / 1000;
iter++;
}
You can see this by inspecting the frame code inside CodePen itself.
They inject shouldStopLoop
calls inside your code.
They have a script called stopExecutionOnTimeout
which does something like this (source from Codepen):
var PenTimer {
programNoLongerBeingMonitored:false,
timeOfFirstCallToShouldStopLoop:0, // measure time
_loopExits:{}, // keep track of leaving loops
_loopTimers:{}, // time loops
START_MONITORING_AFTER:2e3, // give the script some time to bootstrap
STOP_ALL_MONITORING_TIMEOUT:5e3, // don't monitor after some time
MAX_TIME_IN_LOOP_WO_EXIT:2200, // kill loops over 2200 ms
exitedLoop:function(o) { // we exited a loop
this._loopExits[o] = false; // mark
},
shouldStopLoop:function(o) { // the important one, called in loops
if(this.programKilledSoStopMonitoring) return false; // already done
if(this.programNoLongerBeingMonitored)return true;
if(this._loopExits[o]) return false;
var t=this._getTime(); // get current time
if(this.timeOfFirstCallToShouldStopLoop === false)
this.timeOfFirstCallToShouldStopLoop = t;
return false;
}
var i= t - this.timeOfFirstCallToShouldStopLoop; // check time passed
if(i<this.START_MONITORING_AFTER) return false; // still good
if(i>this.STOP_ALL_MONITORING_TIMEOUT){
this.programNoLongerBeingMonitored = true;
return false;
}
try{
this._checkOnInfiniteLoop(o,t);
} catch(n) {
this._sendErrorMessageToEditor(); // send error about loop
this.programKilledSoStopMonitoring=false;
return true; // killed
}
return false; // no need
},
_sendErrorMessageToEditor:function(){/*... */
throw "We found an infinite loop in your Pen. We've stopped the Pen from running. Please correct it or contact [email protected].";
};
If you want to run it yourself - JSBin has similar functionality and they have open sourced it as the loop-protect library - under 500 LoC.
It's just codepen
script runner restrictions.
I run script in Chrome Developer Tools
and in Node.JS REPL
- all seems ok.
Codepen docs
avg
not changing is due to the precision of floating point numbers. At some point, the value ofavg
gets so large that adding a small number to it has no effect, because of the bit size of the mantissa. – Anders Marzi Tornblad Commented Apr 5, 2016 at 12:15i<114000000
? codepen without any arrays – vp_arth Commented Apr 5, 2016 at 12:17Math.random() * 1000000 / 1000
should be in half cases more than 500, which is definitely not enough to be swallowed by rounding errors/precision for such a small number as1261167461.290721
(for my current run) – zerkms Commented Apr 5, 2016 at 12:20.push()
fails), there will be noarr[i]
to fetch the.x
property from, which is more likely the reason foravg
not growing. If all calls to.push()
had succeeded,avg
would still be off because of float errors. But I still don't know whyavg
is unchanged after the second loop. – Anders Marzi Tornblad Commented Apr 5, 2016 at 12:25