Optymalizacja kodu Java

0

Witam.

Chciałbym się dowiedzieć z waszej strony jakie znacie metody na optymalizacje kodu w Java.
Znalazłem w cioci wikipedi zestaw: http://pl.wikipedia.org/wiki/Optymalizacja_kodu_wynikowego i także prosiłbym czy ktoświe jak niektóre (te bardziej trudniejsze) wykorzystać w Javie.

Z góry dziękuję i pozdrawiam.

1

Pierwsze i najważniejsze: uruchamiamy profiler i patrzymy gdzie można coś optymalizować...

0

Co prawda nie zawsze mamy możliwość skorzystać z profesjonalnego środowiska.
Bardziej chodziło o metody typu: korzystanie z wartości bitowych zamiast liczbowych.

1

NIGDY nie należy tak podchodzić do optymalizacji. Czemu? Bo generujesz w ten sposób zagmatwany, gówniany kod, którego nie da się utrzymać a zyskujesz przyspieszenie rzędu 0,5%.
Optymalizuje się TYLKO miejsca wskazane przez profiler, bo tylko one mogą dać sensowne przyspieszenie. Poza tym warto mieć na uwadze utrzymanie kodu - czasem lepiej żeby było trochę wolniejsze, ale czytelne.
Stosowanie wartości bitowych zamiast liczbowych to najwyżej optymalizacja względem pamięci, która w 99% przypadków nie ma sensu ani zastosowania.

Zauważ zresztą że art który podałeś to jest opis optymalizacji jakie stosuje kompilator (!) a nie człowiek.

0

Może to nie to czego oczekiwałeś, ale:

Łączenie pętli
Nie rób tak(chyba, że już naprawdę musisz i dobrze opiszesz), bo łatwo tym narobić burdel z którym Ty lub inni będą się później męczyć, a poza tym jest to optymalizacją tylko jednowątkową, jeżeli masz(a prawie na pewno masz) wiele wątków do dyspozycji, to postępowanie jest wprost przeciwne.

W praktyce optymalizacja polega na tym, że piszesz program/funkcję, później uruchamiasz to w profilerze i eliminujesz wąskie gardła.
Np masz funkcję która wykonuje Ci się 10s, z czego jedna pętla 8s, to wtedy olewasz resztę, i zajmujesz się tylko tą pętlą.
Jeżeli szybkość działania jest kluczowa to i tak bez wstawek w C(lub całości w C) się nie obędzie, ale przeważnie(w korpo) ważniejsza jest szybkość udostępnienia funkcjonalności niż szybkość samej funkcjonalności, a szukając ręcznie i optymalizując wszytko po kolei "zmarnujesz" tylko czas na projekt i szef(często nie mający pojęcia o programowaniu) nie będzie zadowolony.

Polecam przeczytać:
http://www.javablog.eu/java/jmeter-testujemy-wydajnosc/
http://stackoverflow.com/questions/9288787/java-line-by-line-method-function-profiling-profiler-or-eclipse-plugin

4
  1. Efektywny algorytm.
  2. Jeszcze efektywniejszy algorytm.
  3. Na pewno da się lepiej - ulepszyć algorytm.
  4. Uruchomić profiler i zobaczyć gdzie muli.
  5. A mówiłem, że te zagnieżdżone pętle będą się mulić - użyć lepszego algorytmu.
  6. Nie da się zrobić lepszego algorytmu? Zoptymalizować struktury danych i goto 1.
  7. Zrównoleglić algorytm i goto 1.
  8. Dla zaawansowanych: Jeśli struktury danych i algorytmy są w porządku, to można się pobawić w mikrooptymalizacje.

Mikroooptymalizacje w Javie:

  1. Unikać alokacji obiektów przez new.
  2. Nie stosować blokad i synchronizacji.
  3. W alg. wielowątkowych wyrównywać rozmiar obiektów do wielokrotności 32 lub 64 bajtów (false sharing).
  4. Uważać na autoboxing.
  5. Dla bardzo zaawansownaych: W przypadku wątpliwości obejrzeć sobie kod maszynowy (nie bajtkod) i zobaczyć, czy HotSpot nie robi jakiś głupot.
  6. Upychać struktury danych tak aby było jak najmniej referencji a rozmiar jak najmniejszy (dodatkowa zaleta - mniej wywołań new).
  7. W przypadku obróbki dużych ilości surowych danych, warto wyrzucić je poza stertę JVM, aby odciążyć GC.
  8. Stosować odpowiednio wydajne biblioteki np. Javolution zamiast kolekcji standardowych.
  9. Starać się unikać zbyt wielu poziomów abstrakcji i wywołań wirtualnych. JVM jest ogólnie niezły w zwijaniu funkcji wirtualnych, ale jak ma tego za dużo to jednak czasem lepiej mu pomóc.
  10. Starać się uporządkować dane tak, aby jak najefektywniej wykorzystać cache, ułatwić pracę prefetchera i jednostki przewidywania skoków. Tu też jednak na ogół cudów nie ma, ale wszystko zależy od konkretnego algorytmu. W skrajnych przypadkach optymalizacje tego typu mogą dać zysk 3-5x.
  11. W bardzo szczególnych przypadkach zaimplementować fragmenty w C/C++ i wywoływać przez JNA/JNI (ale uwaga na narzut samego JNA/JNI), jednak jeśli przyłożyliśmy się do wszystkich poprzednich punktów, zysk z tego na ogół jest mizerny (można urwać kilka %).

Jeżeli szybkość działania jest kluczowa to i tak bez wstawek w C(lub całości w C) się nie obędzie

Tu bym bardzo, ale to bardzo uważał. W 99% przypadków, jeśli nie jest super ekspertem od pisania wydajnego kodu w C/C++, i przeniesiesz kod bezpośrednio tylko dopasowując skłądnię, to tylko pogorszysz wydajność a nie poprawisz. W C/C++ może Cię ugryźć bardzo mocno aliasing, fragmentacja pamięci, powolność new/delete (malloc/free), gorszy inliner wywołań wirtualnych.

Używanie C/C++ ma sens tylko jeśli:

  1. chcesz wykorzystać ręcznie specjalne instrukcje procesora (np. SSE4), których HotSpot nie wykorzystał (ale musisz najpierw sprawdzić, czy faktycznie tak się stało). Tu raczej bez schodzenia do poziomu asma się nie obędzie, bo kompilatory C wcale już nie są takie lepsze w wektoryzowaniu pętli niż serwerowy HotSpot (tj. jedne i drugie są dość cienkie). Tu jest fajny przykład: http://lemire.me/blog/archives/2012/07/23/is-cc-worth-it/

  2. musisz mieć pełną kontrolę nad ułożeniem danych w pamięci tak aby zmaksymalizować wykorzystanie cache (C++ placement new, eliminacja wskaźników, precyzyjne dopasowywanie rozmiaru obiektów do rozmiaru linii cache itp).

  3. Chesz utrzymać wysoki poziom abstrakcji a zarazem mocno specjalizować kod - wtedy używasz mocno C++ templates i olewasz polimorfizm czasu wykonania.

W każdym razie wiara, że po przepisaniu 1:1 kodu Javy na C/C++ kompilator automagicznie przyspieszy go o rząd wielkości, jest wielce naiwna.

0

Do postu @Krolik dodałbym jeszcze optymalizację na poziomie konfiguracji GC :)

0

Stosowanie wartości bitowych zamiast liczbowych to najwyżej optymalizacja względem pamięci, która w 99% przypadków nie ma sensu ani zastosowania.

Za to w tym 1% przypadków może to spowodować, że gorąca struktura danych zmieści się w całości w cache L1 zamiast wystawać poza cache L3 i program jednak dość mocno przyspieszy (oczywiście czy to będzie 5x, czy tylko 5% zależy od konkretnego przypadku). Przy obecnych architekturach pamięci z niejednorodnym czasem dostępu optymalizacje względem pamięci optymalizacjami wydajnościowymi.

Oczywiście takie optymalizacje zostawiam na deser, jak już wiem, że nic nie zrobię algorytmicznie.

0

@Krolik
Chodziło mi o to, że jak coś ma być superszybkie to nie jen język, tylko jednak coś działające na sprzęcie, więc bez spiny z optymalizowaniem wszystkiego.

@Dziekibardzo
Z optymalizacji już w kodzie: http://kaioa.com/node/59
W skrócie: 4 sklejenia(lub więcej) w środku pętli to zły pomysł.

0

W javie jest kilka zasad tworzenia pisania kodu, które pozwalają na pewne "optymalizacje" np. dotyczące inicjowania zmiennych. Jednak by zrobić "prawdziwą" optymalizację należy zaprzyjaźnić się co najmniej z jmap, które jest takim bardzo prostym profilerem pamięci.

0

Mikroooptymalizacje w Javie:

  1. Unikać alokacji obiektów przez new.

To jest tricky moim zdaniem.

  1. Od Javy 6u24 (czy coś koło tego) jest domyślnie włączone Escape Analysis. Technika ma duży potencjał i moim zdaniem przy rozwiniętych optymalizacjach w kompilatorze kod wykonujący dużo alokacji może być nawet szybszy niż ten używający jakichś obiektów trwałych. Przede wszystkim, przy małych obiektach w ciasnych pętlach kompilator może w ogóle wyciąć jakąkolwiek alokację i 'zaalokować' obiekt wprost w rejestrach procesora.
  2. Sztuczne wydłużanie życia obiektów może spowodować trudności w odśmiecaniu - odśmiecanie najmłodszych generacji jest najszybsze, odśmiecanie najstarszej bardzo powolne, dodatkowo najstarsza generacja nie jest defragmentowana i jej odśmiecanie powoduje freeza (na czas odśmiecania).
  3. Ja jest dużo obiektów trwałych (które służą do zmniejszenia ilości alokacji) to zajmują one pamięć wobec czego mogą zwiększać obciążenie odśmiecacza (w sumie ten punkt jest bardzo powiązany z punktem drugim).

Unikanie new ma sens dużo rzadziej niż przeciętnemu programiście się wydaje, moim zdaniem.

0
  1. Od Javy 6u24 (czy coś koło tego) jest domyślnie włączone Escape Analysis. Technika ma duży potencjał i moim zdaniem przy rozwiniętych optymalizacjach w kompilatorze kod wykonujący dużo alokacji może być nawet szybszy niż ten używający jakichś obiektów trwałych. Przede wszystkim, przy małych obiektach w ciasnych pętlach kompilator może w ogóle wyciąć jakąkolwiek alokację i 'zaalokować' obiekt wprost w rejestrach procesora.

Niestety w praktyce EA działa słabo. Działa tylko w prostych sytuacjach, jeśli obiekt nie wychodzi poza bieżący lokalny zasięg.
EA byłoby niezłe, gdyby potrafiło wyjść poza analizę lokalnego zasięgu.

  1. Sztuczne wydłużanie życia obiektów może spowodować trudności w odśmiecaniu - odśmiecanie najmłodszych generacji jest najszybsze, odśmiecanie najstarszej bardzo powolne, dodatkowo najstarsza generacja nie jest defragmentowana i jej odśmiecanie powoduje freeza (na czas odśmiecania).

Każdy medal ma dwie strony. Zbyt dużo tymczasowych krótkożyjących obiektów zwiększa częstotliwość odśmiecania młodej generacji i może powodować przedwczesne promowanie obiektów do generacji starej. W ten sposób można zapchać starą generację dużo bardziej, niż trzymając w niej na stałe obiekty do powtórnego użycia. Z doświadczenia nie opłaca się robić tego dla bardzo małych obiektów. Ale już dla buforów po kilka, kilkanaście kB, zdecydowanie tak.

Co do samego unikania alokacji - ponowne użycie obiektów to tylko jedna z technik. W pierwszej kolejności trzeba się zastanowić, czy obiekt nam w ogóle jest potrzebny. Raczej to miałem na myśli jako "unikanie alokacji".

A żeby uniknąć freeza, można stosować G1.

1 użytkowników online, w tym zalogowanych: 0, gości: 1