sobota, listopada 07, 2009

Java 32 bit vs. Java 64 bit

Ciekawe rzeczy wychodzą gdy porówna się 32 bitową i 64 bitową Java'e.

Test był prosty, operacja na listach i mapach, tak wyglądała ich deklaracja:
  List list = new ArrayList();
ArrayList aList = new ArrayList();
Map map = new HashMap();
HashMap hMap = new HashMap();

Test na listach składał się z 200 tysięcy dodań do listy stringa [stworzonego wg. mechanizmu "a" [lub "l"] + liczby od 0 do 199999], następnie przeiterowanie tak stworzonej listy i w końcu wykasowanie elementów na podstawie stringa, który posłużył do ich stworzenia.

Dla 32 bitów wygląda to tak [czasy podane są dla wykonania 200 tysięcy operacji danego typu plus pewien narzut na pętlę]:
List:add 196.33 ms
List:iterate 11.33 ms
List:remove 3.67 ms
ArrayList:add 126.33 ms
ArrayList:iterate 9 ms
ArrayList:remove 3.67 ms


Dla 64 bitów zaś tak:
List:add 164 ms
List:iterate 22 ms
List:remove 10.33 ms
ArrayList:add 94 ms
ArrayList:iterate 15.33 ms
ArrayList:remove 9.33 ms

Jak widać dodawanie jest najszybsze w przypadku bezpośredniego użycia ArrayList w 64 bitowej Java'ie, wtedy dodanie 200 tysięcy stringów zajmuje tylko 94 ms, najwolniejsze jest zaś dodanie takiej samej listy stringów do ArrayList widzianej jako List w 32 bitach.
Ale już iterowanie przez listę jest w 64 bitach prawie 2 razy wolniejsze niż w 32 bitowej Java'ie!
Kasowanie zaś jest w 64 bitach wolniejsze o 2.5-3 razy niż w 32 bitach.

Przy okazji widać też, że operowanie na listach z użyciem zmiennych typu ArrayList jest szybsze niż gdy zmienne są bardziej ogólne i są typu List.

W przypadku operacji na mapach wyniki dla 32 bitów wyglądają tak:
Map:put 380 ms
Map:iterate 13.67
Map:remove 77.67
HashMap:put 301.67 ms
HashMap:iterate 12.33 ms
HashMap:remove 80.67 ms

Dla 64 bitów zaś tak:
Map:put 485 ms
Map:iterate 33.67
Map:remove 105.33
HashMap:put 243 ms
HashMap:iterate 25.33 ms
HashMap:remove 103.33 ms

Tutaj już 32 bity okazują się dużo szybsze we wszystkich operacjach.

Dodatkowo uruchomiłem w 32 bitach i 64 bitach program do generowania listy podobnych postów dla tego bloga i okazało się, że tutaj wersja 64 bitowa w dość złożonym problemie analizy podobieństwa postów potrzebowała na tą analizę 29 sekund, a wersja 32 bitowa 32 sekund, czyli różnica prędkości wyniosła około 10% na korzyść 64 bitów.

Także w opisywanych już przeze mnie tu testach z całkowaniem numerycznym Java 64 bitowa okazała się być szybsza od 32 bitowej.
Wyniki przedstawiały się w tym teście następująco:
Java 32bit - 4.7 ms
Java 64bit - 3.8 ms
Czyli 64 bitowa Java szybsza była o jakieś 20-25% od 32 bitowej.

Kolejnym testem było uruchomienie opisywanego tu też kiedyś przeze mnie kodu który renderuje twarz 3D używając cieniowania Gourauda.
I tutaj też 64 bity były szybsze.

Obliczenie jednej klatki zajęło:
Java 32bit 2.03 ms
Java 64bit 1.30 ms

Stworzenie obrazka na podstawie mapy bitowej zajęło:
Java 32bit 4.09 ms
Java 64bit 3.3 ms

Czyli różnica w przypadku obliczeń wyniosła ponad 56%, a w przypadku tworzenia obrazka 24% na korzyść Java'y 64 bitowej.

Myślę, że można więc powiedzieć, że 64 bitowa Java jest jednak szybsza ;-)

Do testów użyłem Sun Java 1.6.0_16 w wersji 32 bitowe i 64 bitowej, wszystko na 64 bitowym Windows 7.


Podobne postybeta
32 bity vs. 64 bity, tym razem C++ ;-)
A JavaScript i tak szybszy ;-)
Nie inicjalizuj rozmiaru kolekcji...
LinkedList w Java'ie to taki miś koala...
OpenOffice.org2GoogleDocs na Apple - pierwsze podejście ;-)

3 komentarze:

  1. Użycie zmiennej List zamiast ArrayList nic nie zmienia, bo implementacją jest w obydwu przypadkach ArrayList.
    Powtórka z javy by się przydała :)

    OdpowiedzUsuń
  2. List to interfejs, a ArrayList to klasa konkretna implementująca ten interfejs. I użycie w kodzie jako typu zmiennej typu interfejsu List zamiast konkretnego typu ArrayList powoduje wygenerowanie różnego bytecode'u i wpływa na prędkość wykonywania kodu.

    Czyli:
    List list = new ArrayList();
    list.add(10);

    wygeneruje inny kod [i wykona się wolniej] niż:
    ArrayList list = new ArrayList();
    list.add(10);

    Co zresztą widać w wynikach, bo z zasady operowanie na zmiennych deklarowanych jak interfejsy jest wolniejsze, po prostu JVM musi dla interfejsu wywołać invokeinterface [i odłożyć na stos 4 wartości], a dla klasy konkretnej woła invokevirtual [i odkłada tylko 2 wartości na stos].

    Czyli ktoś tu sobie musi Java'ę powtórzyć, ale chyba tym razem nie ja ;-)

    OdpowiedzUsuń
  3. Szacunek - kiedyś wykorzystam tę wiedzę ;)

    OdpowiedzUsuń