2016-04-05 12 views
28

Il seguente codice esegue un errore di silenzio logica:Strano JavaScript CodePen con array humongous

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

comportamenti possibili (s):

  • La variabile avg dopo il secondo ciclo for dovrebbe essere raddoppiato e la lunghezza dell'array dovrebbe essere 114 milioni.

  • Si dovrebbe ottenere un errore di memoria.

uscita quando eseguito come uno script:

  • avg non cambia dopo il secondo ciclo for.
  • La lunghezza dell'array non è 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
+0

Una piccola nota: il cambio "avg" è dovuto alla precisione dei numeri in virgola mobile. Ad un certo punto, il valore di 'avg' diventa così grande che aggiungere un numero piccolo ad esso non ha alcun effetto, a causa della dimensione del bit della mantissa. –

+1

potrebbe essere un problema con 'i <114000000'? [codepen senza array] (http://codepen.io/vpArth/pen/eZeGGr) –

+0

@AndersTornblad 'Math.random() * 1000000/1000' dovrebbe essere in mezzo casi più di 500, che non è sicuramente sufficiente per essere ingoiato da errori di arrotondamento/precisione per un numero così piccolo come '1261167461.290721' (per la mia esecuzione corrente) – zerkms

risposta

33

Quando si scrive il codice in Codepen - che in realtà non lo esegue così com'è, ma piuttosto prima si applicano alcune trasformazioni ad esso.

Lo analizzano in un abstract syntax tree, i loop di ricerca e insert instructions explicitly interrompono l'esecuzione del ciclo se è trascorso troppo tempo.

Quando si esegue:

for(let i = 0; i < 114000000; i++){ 
    arr.push(new Point()); 
    avg += arr[i].x/1000; 
} 

Il codice viene eseguito come:

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++; 
} 

È possibile vedere questo ispezionando il codice telaio interno CodePen stessa.

Si iniettano chiamate shouldStopLoop all'interno del codice. Hanno uno script chiamato stopExecutionOnTimeout che fa qualcosa di simile (fonte da 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]"; 
}; 

Se si desidera eseguire da soli - JSBin ha funzionalità simili e hanno open sourced it come libreria di loop di protezione - sotto 500 Loc.

12

Sono solo codepen restrizioni di runner di script.

Corro script in Chrome Developer Tools e in Node.JS REPL - tutto sembra ok.


Codepen docs

+3

source: http://codepen.io/quezo/post/stopping-infinite-loops – Maurize

+0

Perfetto! Questo è stato del tutto inaspettato ... sto rimuovendo la domanda come totalmente fuorviante. – ShrekOverflow

+1

La rimozione della domanda è una cattiva opzione. Può essere utile per alcuni futuri lettori. –