2012-12-28 15 views
7

Mam pętlę, która musi być uruchamiana 200 milionów razy w przeglądarce. Jest to symulator, którego kilka osób musi regularnie używać. Uruchomienie trwa około 15 minut, ale w tym czasie przeglądarki często wyświetlają ostrzeżenie z "zbyt długim skryptem" itd. I całkowicie zawieszają Firefoksa podczas działania. Oznacza to również, że strona nie aktualizuje mojego wskaźnika statusu (który jest tylko liczbą).Zapobieganie blokowaniu przeglądarki JavaScript w dużej pętli

Mam googled "wydajność javascript" i przeczytać pierwsze 4 strony trafień. Niektórzy omawiają nowe słowo kluczowe "yield", ale jest tylko jeden opis i przykład, które uważam za niezrozumiałe, np. "Funkcja zawierająca słowo kluczowe yield jest generatorem, a kiedy go nazwiesz, to parametry formalne są powiązane z rzeczywistymi argumentami, ale ciało nie jest faktycznie oceniane". Czy yield ustępuje UI?

Jednym z niewielu rozwiązań znalazłem jest to stary post, który korzysta z przestarzałej wywoływanego argumentu, a licznik nazywać się: http://www.julienlecomte.net/blog/2007/10/28/

Jednak powyższy przykład nie zawiera żadnych zmiennych pętli lub stan, a kiedy je dodaję, rozpada się, a mój wynik netto zawsze wynosi zero.

To również nie zrobić wyrwy, ale znalazłem kilka innych przykładów, które za pomocą kawałek „Indeks% 100 == 0” przy każdej iteracji. Jednak wydaje się, że jest to powolny sposób. Na przykład. to:

How to stop intense Javascript loop from freezing the browser

Ale to nie ma żadnego sposobu, aby zaktualizować postęp, a nie poddawać się w interfejsie (tak nadal wisi przeglądarkę). Tutaj jest to wersja testowa, która zawiesza przeglądarkę podczas wykonania:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var chunk; 
function Stats() {this.a=0}; 
var stats = new Stats();    
var big; 

var index = 0; 

var process = function() { 
    for (; index < spins; index++) { 
    stats.a++; 
    big = (big/3.6)+ big * 1.3 * big/2.1; 
    console.write(big); 
    // Perform xml processing 
    if (index + 1 < spins && index % 100 == 0) { 
     document.getElementById("result").innerHTML = stats.a; 
     setTimeout(process, 5); 
    } 
    } 
    document.getElementById("result").innerHTML = stats.a; 
}; 


</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="process()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 

i tu jest kolejna próba którym stats.a jest zawsze zero (tak przypuszczam istnieje jakiś problem określania zakresu):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var chunk; 
function Stats() {this.a=0}; 
var stats = new Stats();    

function doIt() { 
    function spin() { 
     for (spinIx=0; (spinIx<chunkSize) && (spinIx+chunk < spins); spinIx++) { 
      stats.a++; 
     } 
    }   

    for (chunk =0; chunk < spins; chunk+=chunkSize){ 
     setTimeout(spin, 5); 
      document.getElementById("result").innerHTML = stats.a; 
     } 
     document.getElementById("result").innerHTML = stats.a; 
} 

</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="doIt()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 

I Spędziłem 48 godzin próbując to zadziałać - albo jestem bardzo głupi, albo to jest bardzo trudne. Jakieś pomysły?

Kilka osób zasugerowało pracowników internetowych. Próbowałem kilka dni, aby uzyskać jego pracy, ale nie mogłem znaleźć podobny przykład, który przechodzi szereg itd. Poniższy kod była moja ostatnia próba dostać pracę, ale wynik jest zawsze 0, gdy powinna być 100000 Tj zawiedzie w taki sam sposób, jak mój drugi przykład powyżej zawodzi.

spinMaster.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
</head> 
<body> 
<script> 
if(typeof(Worker)==="undefined") { 
    document.write("<h1>sorry, your browser doesnt support web workers, please use firefox, opera, chorme or safari</h1>"); 
    } 
var worker =new Worker("spinWorker.js"); 

worker.postMessage({times:1000000}); 

worker.onmessage=function(event){ 
document.getElementById("result").innerHTML=event.data; 
}; 
</script> 

<div id="result">result goes here</div> 
</body> 
</html> 

spinWorker.js

function State() { 
    this.a=0; 
} 

var state = new State(); 

self.addEventListener('message', spin, false); 

function spin(e) { 
    var times, i; 

    times = e.data.times; 
//times = 1000000; // this doesnt work either. 

    for(i;i<times;i++) { 
     state.a++; 
    } 

    self.postMessage(state.a); 
} 

wyjście wypadkową: 0

+4

od JS jest gwintowany zwykle pojedyncze, nie sądzę, istnieje jakikolwiek sposób wokół niego. Jeśli jednak korzystasz tylko z nowszych przeglądarek, możesz zajrzeć do pracowników internetowych, którzy mogą utworzyć nowy wątek do pracy: https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers – kennypu

+0

@kennypu Powinieneś opublikować to jako odpowiedź. –

+1

Twoim problemem z wydajnością może być również stała manipulacja DOM (co 5 ms), która jest BARDZO kosztowna. Spróbuj zaktualizować DOM w różnym tempie, powiedz co 10-20 sekund i zobacz, czy to sprawi, że będzie działał lepiej/szybciej. – elclanrs

Odpowiedz

1

od JS jest pojedynczy gwintowany zwykle, nie sądzę, istnieje jakikolwiek sposób wokół to. Jeśli jednak korzystasz tylko z nowszych przeglądarek, możesz zajrzeć do pracowników internetowych, którzy mogą utworzyć nowy wątek: http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

Jedyną wadą, o której myślę, jest to, że przeczytałem, że trudno jest debuguj Web Workers, ponieważ narzędzia deweloperskie (narzędzia Chrome Dev, o ile nie działają na kanale deweloperskim, firebug), nie obsługują profilowania.

tu jest ładny poradnik jak zacząć grę: http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/

4

sieci pracownicy wydawać się lepszym rozwiązaniem.

Zapisałem to szybko, więc nie wiem, czy to zadziała. Wydajność będzie bardzo zła ...

Edytuj: Zmieniono na ten sam format co plakatu. Przetestowane

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
    var spins = 1000000 
    var chunkSize = 1000; 
    var chunk; 
    function Stats() {this.a=0}; 
    var stats = new Stats();   
    var big = 0.0; 

    var index = 0; 

    function workLoop() { 

     index += 1; 

     stats.a++; 
     big = (big/3.6)+ big * 1.3 * big/2.1; 
     console.log(big); 
     // Perform xml processing 
     document.getElementById('result').innerHTML = stats.a; 
     if (index < spins) { 
      setTimeout(workLoop, 5); 
     } 

    } 



</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="workLoop()"> 
<div id="result">result goes here.</div> 
</body> 
</html> 
+0

Witaj, rissicay, czy mógłbyś wyjaśnić, do czego służy element "if (index)? Moje zrozumienie mówisz" jeśli (index == 0) indext = 1; ", który jest taki sam jak" index + = 1; " –

+0

Tak, masz rację, zrobiłem edycję – rissicay

-1

ten powinien robić to, co chcesz:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var stats = {a:0,spins:0};    

function doIt() { 
    for (var chunk = 0; chunk < chunkSize; chunk++) { 
     stats.a++; 
    } 
    document.getElementById("result").innerHTML = stats.a; 
    if(++stats.spins < spins) setTimeout(doIt,5); 

} 

</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="doIt()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 
1

Masz blisko przykład pracę w 1 badaniu, ale widzę błąd logiczny. W twoim if() musisz powrócić z funkcji, w przeciwnym razie zawsze będzie działać wiele funkcji rywalizujących o ten wątek.

var process = function() { 
    for (; index < spins; index++) { 
    stats.a++; 
    big = (big/3.6)+ big * 1.3 * big/2.1; 
    console.write(big); 
    // Perform xml processing 
    if (index + 1 < spins && index % 100 == 0) { 

document.getElementById("result").innerHTML = stats.a; 
     setTimeout(process, 5); 
     //!!!!! 
     return;//without this it'll keep iterating through the for loop without waiting for the next 5ms 
     //!!!!! 
    } 
    } 

document.getElementById("result").innerHTML = stats.a; 
}; 

Żadna z próbek jak jest poczekać na następny setTimeout (w 2. Przykład ty nadal iteracja swój pętli, aż do pętli jest zakończona, ale w każdym rozmiarze bloku ustawić czas oczekiwania na kolejny Wszystko to robi, opóźniając całą sekwencję o 5 ms, cały czas to cały pileup i wykonuj 5 ms od momentu, gdy twoja pętla for zaczyna iterować)

W sumie wydaje się, że jesteś na dobrej drodze, są tylko małe błędy logiczne w obu przykładach.

+0

Dzięki, to było pomocne, chociaż tak naprawdę nie rozumiem, co się dzieje. Dodałem ten zwrot, a następnie wypisuje każdy numer (np. 1,2,3) zamiast 100,200 itd. Dodałem && (index> 0) do if i dodatkowego indeksu ++ przed powrotem, a teraz wydaje się działać. Dziwne jest to, że skopiowałem ten przykład dosłownie z jednego z kilku opublikowanych przykładów jak to zrobić Wydaje mi się, że opublikowali nie działający kod: –

+0

ya, dobry punkt, ponieważ zaczynasz od wielokrotności 100, indeks> 0 jest potrzebny, aby pierwsza iteracja odniosła sukces (jako 0% 100 == 0). które są gotowe do kopiowania i gotowe, a te, które są wskaźnikami, być może ten, którego użyłeś, był skierowany w kierunku wskaźnika. Jest to jednak powszechny błąd w przypadku funkcji rekursywnych i tak jak w JavaScript, odroczonych pętlach opartych na zmiennych globalnych, takich jak "indeks". No cóż. Więc gdzie teraz powinnaś być? Czy Twoja aplikacja działa zgodnie z oczekiwaniami? – chwagssd

0

Istnieje biblioteka na github dostępna dla tego typu dużych pętli bez blokowania przeglądarki/wątku i bez korzystania z pracowników internetowych.

Nieco eksperymentalne i obsługuje duże pętle rekursywne, ale może działać dla ciebie. Zależy od q.js przy rozwiązywaniu obietnic.

https://github.com/sterpe/stackless.js

Powiązane problemy