czwartek, października 29, 2009

A JavaScript i tak szybszy ;-)

Gdy pisałem posta o prędkości różnych języków w pewnym specyficznym zastosowaniu, którym było liczenie całki z e-x2 kusiło mnie by zrobić test z JavaScriptem, ale uznałem, że to przecież tak wolne jest, że nie ma sensu.........

Jakże się myliłem! :-)

Na moim laptopie [czyli profesor 2.2 GHz Core 2 Duo z 4GB RAM pod Windows 7] średni wyniki wykonania 1 iteracji w której całkowana była funkcja f(x)=e-x2 w przedziale lewostronnie domkniętym od -10 do 10 w 10 tysiącach kroków, a samych iteracji wykonanych było tysiąc, przedstawiają się następująco:
Mozilla Firefox 3.5.4 - 1.380 ms
Google Chrome 4.0.223.11 - 2.514 ms

IE 8 odpadło bo za każdym razem krzyczało, że skrypt wykonuje się zbyt długo, ale gdy trochę go zmieniłem [przez zmniejszenie ilości iteracji z 1000 do 100] to IE 8 potrzebował 15.28 ms.

Jak nie wierzycie to tutaj macie "tester" :-) [Jedna uwaga, odpalajcie go w FF lub Chrome, IE odpada i Opera raczej też].

Dla porównania na tym laptopie wyniki poprzedniego testu wyglądają tak [dane podałem już w komentarzach do poprzedniego posta, ale sprawdziłem je ponownie i w przypadku C przez głupi błąd były one mniejsze o 1 rząd]:
C 32bit [kompilator z Dev-Cpp] - 1.54 ms
C 64bit [lcc-win32 64bit] - 6.97 ms
Java 32bit - 4.7 ms
Java 64bit - 3.8 ms
.NET 64bit - 14 ms
Python 2.5.2 32bit - 10.3 ms
Python 2.5.4 64bit - 10.16 ms

Wyszło więc, że akurat w tym zastosowaniu JavaScript jest wydajniejszy od C ;-)
Nie potrafię wyjaśnić dlaczego tak jest, ale i tak to cieszy ;-)


Podobne postybeta
Java 32 bit vs. Java 64 bit
32 bity vs. 64 bity, tym razem C++ ;-)
C# i Java okazały się szybsza od Pythona :-) [było Java okazała się szybsza od C# i Pythona]
Koszmarek - Small Basic ;-)
Sarkania na C++

9 komentarzy:

  1. Porównałem wyniki na trzech przeglądarkach. Mój Fx - mimo że nadal ma niesamowity wynik - nie robi rzeczy niemożliwych.

    Tym razem przesiadłem się na stacjonarny komputer:

    model name : Intel(R) Pentium(R) D CPU 3.00GHz
    cpu MHz : 2992.832
    cache size : 2048 KB

    Teoretycznie wyniki powinny być mniej więcej 4x lepsze od tego co pisałem wcześniej i tak jest z programem w C

    $ gcc -o speed speed.c -lm
    $ time speed
    speed 2,52s user 0,00s system 98% cpu 2,569 total

    Java na tej maszynie nie popisała się:

    $ time java -server Speed
    6
    java -server Speed 6,74s user 0,08s system 97% cpu 6,984 total

    Przy 4x szybszym procku, tylko 2x lepszy wynik?

    Piszę to tylko by zachować pewien układ odniesienia do poprzedniego postu...

    ... a teraz przechodząc do JavaScript na przeglądarkach:

    Opera 9.6: 24.572
    Google Chrome 4: 4.753
    Firefox 3.5.5pre: 3.849

    Tak więc nieco wolniej niż program w C, ale i tak znacznie szybciej niż Java.

    Zastanawiam się co może być przyczyną tych podejrzanie dobrych rezultatów na Twojej maszynie. Przychodzi mi do głowy jedna rzecz: Kiedyś czytałem o problemach z podobnymi rankingami robionymi w przeglądarce na XP. Przyczyną była niska rozdzielczość zegara systemowego (błąd był właśnie rzędu 2s). Jeśli tak jest to winowajcą była by funkcja Date().getTime()

    OdpowiedzUsuń
  2. Ta niedokładność zegara mogłaby tłumaczyć wyniki, chociaż powinny wtedy one też "pływać" w odpowiedni sposób.
    Załóżmy, że zegar jest "uaktualniany" raz na sekundę, czyli w momencie odczytu czasu początkowego może być zaniżony od 0 do prawie 1 sekundy, to samo w przypadku odczytu przy końcu obliczeń. Najbardziej "korzystny" warunek, czyli taki który najbardziej skróci czas wynikowy to sytuacja gdy pierwszy czas odczytamy dokładnie z 0 przesunięciem, a czas końcowy z maksymalnym, czyli 1 sekundą. Wtedy błąd wynosić będzie maksymalnie 1 sekundę.
    Jeżeli więc tak jest to powinniśmy widzieć znaczącą różnicę w wynikach testów które trwały 1.5 sekundy, a takich, które trwały 14.5 sekundy.
    Zrobiłem więc test trwający 14.5 sekundy :-) Standardowy test na tym komputerze dawał wynik 3.782 ms, w przypadku testu trwającego te 14.5 sekundy jest to 3.6 ms, czyli wynik porównywalny, więc IMHO Date().getTime() jako powód różnic w czasie odpada.
    Ja bym stawiał na różnice w implementacji operacji zmiennoprzecinkowych.

    OdpowiedzUsuń
  3. Znalazłem dziś chwilę by poszukać o potencjalnych problemach z wywołaniem (new Date).getTime()

    Tu masz odnośniki:
    http://ejohn.org/blog/javascript-benchmark-quality/
    http://ejohn.org/blog/accuracy-of-javascript-time/

    Mam nadzieję że będą przydatne.

    Wniosek jest taki że aby uzyskać bardziej miarodajne wyniki trzeba zadbać o to by mierzony czas był jak najdłuższy.. w przeciwnym razie można właśnie niechcący zaniżyć wyniki.

    OdpowiedzUsuń
  4. On tu pisze o niedokładności pomiaru wynikłej z tego faktu, że przeglądarce coś może przeszkodzić, nie o samym niedokładnym działaniu new Date().getTime().

    Przed chwilą zrobiłem testy i wychodzi, że rozdzielczość zegara JavaScriptu musi być lepsza niż 3ms. Zrobiłem to tak, że 50 razy zmierzyłem pojedyncze wykonanie funkcji calc(), zmierzyłem też 50 razy wykonanie 5, 10, 50 i 100 razy funkcji calc(). Wyniki są bardzo podobne dla każdego testu, największa różnica między wynikami wynosi 4.5%.

    IMHO prawdziwe niedokładności to są dopiero z setTimeout() ;-)

    OdpowiedzUsuń
  5. Zgadza się, szczególnie jeśli piszesz o przeglądarce Firefox, która została mocno poprawiona właśnie po tym co napisał John Reisig.

    Kłopoty z dokładnością pomiaru dotyczą nie tylko przeglądarek, ale także systemu operacyjnego właśnie czytam:
    http://www.kernel.org/doc/man-pages/online/pages/man7/time.7.html

    To akurat dotyczy Linuksa, nie znalazłem podobnego tekstu na temat Windows.

    Problem pomiarem czasu za pomocą funkcji systemowych polega na tym że mają one mocno ograniczoną dokładność i co gorsze różną zależnie od platformy (system, przeglądarka).

    Wydaje mi się że najpewniejszą metodą było by wyliczenie ilości iteracji na minutę - w ten sposób potencjalne błędy wynikające z wspomnianych wcześniej "szumów", lub niedokładności zegara nie faworyzowały by żadnego narzędzia.

    OdpowiedzUsuń
  6. ... ach, zapomniałem dodać: Generalnie zgadzam się z Twoimi argumentami, ale wciąż czuję że przy tym konkretnym algorytmie JavaScript nie może być szybszy od C.

    OdpowiedzUsuń
  7. Kiedyś najdokłądniejszą metodą było użycie rozkazu RDTSC procesora :-) ponieważ podawało to dokładnie liczbę taktów. Problemy pojawiły się w momencie wejścia systemów wielozadaniowych.... Choć prawdę mówiąc już funkcje podpięte pod przerwania zegarowe robiły pewne problemy ;-)

    Trudno powiedzieć czemu tu zadziałało to lepiej w JavaScript niż w C, różnice są zbyt znaczące i zbyt "konsekwentne" by uznać je za przypadek. Myślełem, że może chodzi o arytmetykę zmiennoprzecinkową, ale wyniki są tu i tu identyczne więc to nie to.
    Pewnie kompilator używany przez Dev-C++ nie jest zbyt dobry, jeśli mi się uda spróbuję skompilować ten kod przy pomocy innego lub innych kompilatorów i zobaczymy :-)

    OdpowiedzUsuń
  8. Kompilator C ma tu coś do powiedzenia ;-)
    Na maszynie gdzie FF potrzebował 1.380 ms, program skompilowany w Dev-C++ [port GCC na Windows] potrzebował 1.57 ms, a program skompilowany w VC++ potrzebował już tylko 0.98 ms ;-)

    OdpowiedzUsuń
  9. .. i tu chyba mamy odpowiedź - Firefox na Windows został skompilowany za pomocą VC++ (z domyślnymi ustawieniami) co jak mniemam tłumaczy zbliżone wyniki.

    Natomiast na mojej maszynie kompilowany był za pomocą GCC (również z domyślnymi ustawieniami) i dla tego nie widziałem podobnego efektu.

    Wieczorem napiszę więcej, bo zamierzam zrobić jeszcze parę małych testów..

    OdpowiedzUsuń