Benchmark: Ruby 1.8, Ruby 1.9, JRuby 1.3 RC2

Posted by Jacek Galanciak on

Wiele się mówi o szybkości Rubiego. Na pewno dzięki temu jest coraz mniej osób, które uważają, że jest to język zbyt wolny do aplikacji webowych - Merb w końcu pokazał, że potrafi być szybszy niż pythonowi konkurenci (Django, Pylons), a PHP (także pod względem wydajności frameworków) to już nie margines, a zwykła patologia.

Ostatnio modne są microbenchmarki, testujące pojedynczy aspekt języka - wyjątki, rekurencję itp. Jest to oczywista bzdura, która tak naprawdę niczego nie mówi, dlatego postanowiłem zmierzyć wydajność tych języków w czymś realnym.

Co na pokładzie?

C2D 2.4 GHz, 4GB RAM. System operacyjny: Mac OS X 10.5.7

$ ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.7.0]
Ruby Enterprise Edition 20090520
$ ruby19 -v
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-darwin9.6.0]
$ jruby -v
jruby 1.3.0RC2 (ruby 1.8.6p287) (2009-05-27 46598e4) 
(Java HotSpot(TM) 64-Bit Server VM 1.6.0_07) [x86_64-java]

Runda pierwsza: rozpoznawanie samogłosek w głosie ludzkim

Coś całkiem mocno wykraczające poza zwykłą pętlę for. Rozpoznawanie następuje oczywiście poprzez sztuczne sieci neuronowe, a konkretniej: wielowarstwowe perceptrony ze wsteczną propagacją błędów. Pomijając szczegóły implementacyjne (w końcu chodzi o wydajność), warto tylko zaznaczyć, że czas uczenia sieci został wydłużony do absurdalnej liczy setek tysięcy cykli uczenia się całkiem niemałej paczki danych treningowych. Wszystko po to, by JVM od JRubiego zdążyła się “rozgrzać” i pokazać pazurki. Jak ostre są te pazurki? Porównacie w testach wydajności poniżej:

Ruby 1.8

real    25m11.525s
user    25m6.682s
sys 0m1.949s

Ruby 1.9

real    12m39.138s
user    12m38.201s
sys 0m1.073s

JRuby

real    8m15.320s
user    8m12.577s
sys 0m4.128s

Tak jak się mówi w wielu miejscach, Ruby 1.9 jest dwukrotnie szybszy niż Ruby 1.8. Natomiast JRuby jest szybszy od Ruby 1.9 o 37% , co jest dość znaczną przewagą.

Runda druga: Rails

Nic wielkiego, testowana jest aplikacja z jedną akcją renderującą zwykły szablon. Nie używamy bazy danych. Całość serwowana przez pojedynczego Mongrela. W przypadku Ruby 1.9 należało wprowadzić parę zmian w kodzie C i .rb, aby rozszerzenie dało radę się skompilować. W przypadku JRubiego serwowanie aplikacji na Mongrelu jest, delikatnie mówiąc, nietypowe, ale potrzebne przy równych (w miarę miarodajnych) testach.

Parametr c mówi o ilości jednoczesnych połączeń podczas testów.

Ruby 1.8

-c 1: 582.73 [#/sec]
-c 5: 548.83 [#/sec]
-c 20: 524.57 [#/sec]

Ruby 1.9

-c 1: 548.06 [#/sec]
-c 5: 501.29 [#/sec]
-c 20: 476.33 [#/sec]

JRuby

-c 1: 365.03 [#/sec]
-c 5: 493.89 [#/sec]
-c 20: 486.92 [#/sec]

Bardzo dziwną rzeczą jest to, że Ruby 1.9 okazał się wolniejszy niż Ruby 1.8! JRuby delikatnie przewyższa 1.9 szybkością przy dużych obciążeniach, ale nadal jest to wolniej niż 1.8.

Runda trzecia: Merb

Typ aplikacji taki sam jak w przypadku Railsów. Pamiętajmy, że tylko Merb 1.1 (edge!) działa z wersją 1.9.

Ruby 1.8

-c 1: 953.38 [#/sec]
-c 5: 922.15 [#/sec]
-c 20: 915.56 [#/sec]

Ruby 1.9

-c 1: 1146.85 [#/sec]
-c 5: 1110.52 [#/sec]
-c 20: 1079.74 [#/sec]

JRuby

-c 1: 772.88 [#/sec]
-c 5: 1109.98 [#/sec]
-c 20: 1079.29 [#/sec]

Tutaj wyniki Ruby 1.9 i JRuby są porównywalne. Nie da się ukryć, że Merb jest lepiej napisany niż Railsy, dlatego im lepsza implementacja tym większy skok wydajności. Rails3 ma mieć ulepszone bebechy, dlatego możemy się spodziewać, że wydajność Rails 3 będzie porównywalna z Merbem.

Co to wszystko znaczy?

  • JRuby jest w pełni gotowy do produkcji i jest całkowicie kompatybilny (poza paroma gemami z rozszerzeniami w C) z wersją 1.8. Oznacza to mniej więcej tyle, że w większości przypadków, o ile mamy dużo RAM-u na naszym serwerze, możemy za darmo znacząco zwiększyć wydajność aplikacji zwyczajnie poprzez zmianę implementacji języka na Javową
  • JRuby 1.4 będzie jeszcze szybszy
  • Java 7 będzie posiadała w swoich bebechach wiele ficzerów, które ułatwiają tworzenie na jej bazie języków dynamicznych - oznaczać to będzie prawdopodobne wyrzucenie części kodu z JRubiego, bo będzie on już w samej platformie Java. Takie zabieg prawdopodobnie zwiększy wydajność JRubiego.
  • Tradycyjnie, nowa edycja Javy może zwiększyć jej wydajność, na czym skorzysta JRuby.
  • JRuby nie wymaga zgodności gemów z wersją 1.9, a daje porównywalny kop wydajnościowy, co wersja 1.9. Mam nadzieję, że te słowa nie zdemotywują nikogo do reimplementacji swoich bibliotek pod wersję 1.9… Więcej info na isitruby19.com.
  • Testy dotyczyły tylko trzech implementacji. Niedługo może się pojawić MagLev, Rubinius i MacRuby
  • MacRuby jest projektem wyjątkowo ciekawym. Docelowo ma być kompatybilny z każdym POSIXowym systemem (a więc nie tylko Mac OS X), a także rezygnować z wirtualnej maszyny YARV na rzecz LLVM. Będzie to olbrzymi kop wydajnościowy. Już teraz trunkowa wersja 0.5 potrafi być paręnaście razy szybsza od konkurentów, a skompilowany (tak jest, będzie można kompilować do binarki!) generator ciągu Fibonacciegojest szybszy niż… implementacja w Objective-C! Do tego dochodzi dojrzały generational garbage collector oraz obsługa natywnych wątków. Nie byle jaka implementacja!