2016-04-05 14 views
28

Poniższy kod wykonuje cichy błąd logiczny:Strange JavaScript w CodePen z humongous tablic

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

Możliwa zachowanie (a):

  • Zmienna avg po drugiej pętli for należy podwoić, a długość tablicy powinna wynosić 114 milionów.

  • Powinienem dostać błąd pamięci.

Wyjście po uruchomieniu jako skrypt:

  • avg nie zmienia się po drugiej pętli for.
  • Długość tablicy nie wynosi 114 mil (Chrome 2-3 M, Firefox Dev 5 Mil, MS Edge 788k).
+0

Mała nuta: "średnia" nie zmienia się z powodu precyzji liczb zmiennoprzecinkowych. W pewnym momencie wartość 'avg' staje się tak duża, że ​​dodanie do niej niewielkiej liczby nie ma żadnego wpływu ze względu na rozmiar bitowy mantysy. –

+1

może być problem z 'i <114000000'? [codepen bez żadnych tablic] (http://codepen.io/vpArth/pen/eZeGGr) –

+0

@AndersTornblad 'Math.random() * 1000000/1000' powinno być w połowie przypadków większe niż 500, co zdecydowanie nie jest wystarczające do połknięcia przez zaokrąglenie błędów/precyzji dla tak małej liczby jak '1261167461.290721' (dla mojego bieżącego uruchomienia) – zerkms

Odpowiedz

33

Podczas pisania kodu w Codepen - faktycznie nie wykonać go jak jest, lecz najpierw zastosować pewne przekształcenia do niego.

Parse to w abstract syntax tree, znajdź pętle i insert instructions explicitly, aby zatrzymać wykonywanie pętli, jeśli minęło zbyt dużo czasu.

Kiedy zrobić:

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

Twój kod działa jako:

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

Można zobaczyć, sprawdzając kod ramki wewnątrz samego CodePen.

Wstrzykują one połączenia shouldStopLoop w swoim kodzie. Mają skrypt o nazwie stopExecutionOnTimeout który robi coś takiego (źródło z 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]"; 
}; 

Jeśli chcesz uruchomić go samodzielnie - JSBin posiada podobną funkcjonalność i mają open sourced it jak biblioteki pętli-Protect - poniżej 500 LoC.

12

To tylko codepen ograniczenia runnera.

Uruchomię skrypt w Chrome Developer Tools iw Node.JS REPL - wszystko wydaje się być w porządku.


Codepen docs

+3

źródło: http://codepen.io/quezo/post/stopping-infinite-loops – Maurize

+0

Perfect! To było całkowicie nieoczekiwane ... Czy usunięcie pytania jest całkowicie mylące. – ShrekOverflow

+1

Usunięcie pytania jest złym rozwiązaniem. Może być pomocny dla niektórych przyszłych czytelników. –

Powiązane problemy