Czy Java jest naprawdę wolna?

180

Java ma pewną reputację jako powolna .

  • Czy Java jest naprawdę wolna?
  • Jeśli tak, dlaczego? Gdzie jest (lub było) wąskie gardło? Czy to z powodu niewydajnych maszyn JVM? Zbieranie śmieci? Czyste biblioteki kodów bajtowych zamiast kodu C w JNI? Wiele innych języków ma te funkcje, ale nie mają tej reputacji powolności.
Stefano Borini
źródło
35
ludzie się denerwują ... nie widzą, jak to może być subiektywne, ani sprzeczne. Zastanawiam się, czy pytanie typu „dlaczego sortowanie bąbelkowe jest powolne” uzyskałoby ten sam wynik. Zadałem pytanie techniczne i chciałem uzyskać odpowiedzi techniczne (które otrzymałem), ale zamknięcie pytania jako subiektywnego i kłótliwego jest śmieszne.
Stefano Borini,
1
Przeczytałem większość najważniejszych komentarzy i żadne z nich nie wydaje się odnosić do rażącego faktu, że aplikacje komputerowe oparte na C # GUI działają znacznie szybciej niż jakiekolwiek aplikacje komputerowe oparte na GUI Java, w tym nowoczesne.
BobTurbo,
3
Jako twórca stron internetowych po stronie klienta, który zajmował się formami internetowymi .net, .net MVC, PHP, Rails, Django i szeroką gamą wszystkiego oprócz Springa (który, jak słyszałem, jest dobry) w Javie, oczekuję słabej wydajności / architektury z zaplecza zbudowanego przez zespoły Java. Podejrzewam, że prawdziwym problemem nie są testy porównawcze, ale kwestia po prostu bzdury przeciętnych twórców Javy. To nie wina tego języka. To nie wina programistów Java, którzy faktycznie doskonalą swoje umiejętności i uczą się języków innych niż Java. Może to jednak być wina firmy Sun, certyfikatów, lat 90. i ogólnie branży IT.
Erik Reppen

Odpowiedzi:

236

Współczesna Java jest jednym z najszybszych języków, mimo że wciąż jest świnią pamięci. Java miał reputację powolny, ponieważ zajmowało dużo czasu dla VM do uruchomienia.

Jeśli nadal uważasz, że Java działa wolno , zapoznaj się z wynikami testów wydajności . Ściśle zoptymalizowany kod napisany w skompilowanym z wyprzedzeniem języku (C, Fortran itp.) Może go pobić; jednak Java może być ponad 10 razy szybsza niż PHP, Ruby, Python itp. Istnieją określone obszary, w których może pokonać popularne skompilowane języki (jeśli używają standardowych bibliotek).

Obecnie nie ma usprawiedliwienia dla „wolnych” aplikacji Java. Winni są programiści i starsze kody / biblioteki, znacznie bardziej niż język. Obwiniaj też wszystko „przedsiębiorstwo”.

Aby być sprawiedliwym wobec tłumu „Java jest wolny”, oto obszary, w których wciąż jest wolny (zaktualizowany do 2013 r.):

  • Biblioteki są często pisane pod kątem „poprawności” i czytelności, a nie wydajności. Moim zdaniem jest to główny powód, dla którego Java nadal ma złą reputację, szczególnie po stronie serwera. To sprawia, że ​​problemy z ciągiem są wykładniczo gorsze. Niektóre proste błędy są częste: obiekty są często używane zamiast prymitywów, co zmniejsza wydajność i zwiększa wykorzystanie pamięci. Wiele bibliotek Java (w tym standardowe) często tworzy ciągi, zamiast ponownie wykorzystywać zmienne lub prostsze formaty (char [] lub StringBuffer). Jest to powolne i tworzy mnóstwo śmieci do zebrania później. Aby to naprawić, sugeruję programistom korzystanie z prymitywnych kolekcji, a zwłaszcza bibliotek Javalution, tam gdzie to możliwe.

  • Operacje na łańcuchach są nieco powolne. Java wykorzystuje niezmienne obiekty łańcuchowe zakodowane w UTF-16 . Oznacza to, że potrzebujesz więcej pamięci, więcej dostępu do pamięci, a niektóre operacje są bardziej złożone niż w przypadku ASCII (C, C ++). W tamtym czasie była to właściwa decyzja dotycząca przenośności, ale wiąże się to z niewielkim kosztem wydajności. UTF-8 wygląda teraz na lepszy wybór.

  • Dostęp do tablicy jest nieco wolniejszy w porównaniu do C, ze względu na sprawdzanie granic. Kiedyś kara była duża, ale teraz niewielka (Java 7 optymalizuje wiele zbędnych kontroli granic).

  • Brak arbitralnego dostępu do pamięci może spowolnić niektóre operacje we / wy i przetwarzanie na poziomie bitów (na przykład kompresja / dekompresja). Jest to teraz funkcja bezpieczeństwa większości języków wysokiego poziomu.

  • Java zużywa dużo więcej pamięci niż C, a jeśli twoja aplikacja jest związana z pamięcią lub przepustowością pamięci (buforowanie itp.), Powoduje to spowolnienie. Wadą jest to, że alokacja / dezalokacja płonie szybko (wysoce zoptymalizowana). Jest to obecnie cecha większości języków wysokiego poziomu oraz ze względu na obiekty i użycie GC zamiast jawnego przydziału pamięci. Plus złe decyzje dotyczące biblioteki.

  • I / O oparte na strumieniach jest powolne ze względu na (IMO, zły wybór) wymaganie synchronizacji przy każdym dostępie do strumienia. NIO naprawił to, ale korzystanie z niego jest uciążliwe. Można obejść ten problem, wykonując odczyt / zapis do tablicy zamiast elementu na raz.

  • Java nie zapewnia tej samej funkcji niskiego poziomu co C, więc nie można używać brudnych sztuczek asemblera w celu przyspieszenia niektórych operacji. Zapewnia to przenośność i jest teraz cechą większości języków wysokiego poziomu.

  • Często zdarza się, że aplikacje Java są powiązane z bardzo starymi wersjami JVM. Zwłaszcza po stronie serwera. Te stare maszyny JVM mogą być niezwykle nieefektywne w porównaniu do najnowszych wersji.

Ostatecznie Java została zaprojektowana tak, aby zapewnić bezpieczeństwo i przenośność kosztem pewnej wydajności, a dla niektórych naprawdę wymagających operacji pokazuje. Większość reputacji powolności nie jest już zasłużona.


Istnieje jednak kilka miejsc, w których Java jest szybsza niż większość innych języków:

  • Alokacja i alokacja pamięci są szybkie i tanie. Widziałem przypadki, w których 20% SZYBCIEJ (lub więcej!) Przydziela nową macierz o wielu kB niż ponownie używa pamięci podręcznej.

  • Tworzenie instancji obiektów i funkcje obiektowe są niezwykle szybkie w użyciu (w niektórych przypadkach szybsze niż C ++), ponieważ zostały zaprojektowane od samego początku. Wynika to częściowo z dobrego GC, a nie z jawnej alokacji (co jest bardziej przyjazne dla wielu alokacji małych obiektów). Można napisać kod C, który to osiąga (poprzez niestandardowe zarządzanie pamięcią i wydajne wykonywanie malloc), ale nie jest to łatwe.

  • Wywołania metod są w zasadzie bezpłatne, aw niektórych przypadkach szybsze niż kod dużej metody. HotSpot kompilator używa informacji w celu zoptymalizowania wykonywania wywołań metod i ma bardzo wydajnego inline. Korzystając z dodatkowych informacji o wykonaniu, może czasem przewyższyć kompilatory z wyprzedzeniem, a nawet (w rzadkich przypadkach) ręczne wstawianie. Porównaj z C / C ++, gdzie wywołania metod wiążą się z niewielkim spadkiem wydajności, jeśli kompilator zdecyduje się nie wstawiać.

  • Synchronizacja i wielowątkowość są łatwe i wydajne. Java została zaprojektowana tak, aby od początku była świadoma wątków, i to pokazuje. Nowoczesne komputery zazwyczaj mają wiele rdzeni, a ponieważ wątki są wbudowane w język, możesz bardzo łatwo skorzystać. Zasadniczo dodatkowe zwiększenie prędkości od 100% do 300% w porównaniu ze standardowym, jednowątkowym kodem C. Tak, starannie napisane wątki C i biblioteki mogą to pobić, ale dla programisty jest to dużo dodatkowej pracy.

  • Ciągi zawierają długość: niektóre operacje są szybsze. To bije za pomocą ciągów rozdzielanych znakami zerowymi (często w C). W Javie 7 Oracle wyjęło optymalizację String.subString (), ponieważ ludzie używali jej głupio i dostawali się wycieki pamięci.

  • Kopiowanie tablicy jest wysoce zoptymalizowane. W najnowszych wersjach Java używa ręcznie dostrajanego asemblera dla System.arraycopy. Rezultat jest taki, że w operacjach wymagających dużej ilości arraycopy / memcopy widziałem, jak mój kod bije odpowiednik w C o rozsądne marginesy.

  • Kompilator JIT jest inteligentny w używaniu pamięci podręcznej L1 / L2 . Programy kompilowane z wyprzedzeniem nie mogą w czasie rzeczywistym dostosowywać swojego kodu do konkretnego procesora i systemu, na którym działają. JIT zapewnia w ten sposób kilka bardzo wydajnych transformacji pętli.

Kilka innych faktów historycznych przyczyniło się do reputacji „Java jest wolna”:

  • Przed kompilacją JIT (Java 1.2 / 1.3) język był tylko interpretowany, a nie kompilowany, a zatem bardzo powolny.
  • Kompilacja JIT wymagała czasu, aby stać się wydajnym (znaczne ulepszenia w każdej wersji)
  • Ładowanie klas stało się o wiele bardziej wydajne na przestrzeni lat. Kiedyś był dość nieefektywny i powolny podczas uruchamiania.
  • Kod Swing i interfejs użytkownika nie korzystały z natywnego sprzętu graficznego.
  • Huśtawka jest po prostu okropna. Winię AWT i Swing za to, że Java nigdy nie przyłapała się na pulpicie.
  • Częste wykorzystanie synchronizacji w klasach bibliotek; niezsynchronizowane wersje są teraz dostępne
  • Aplety ładują się wiecznie, ponieważ przesyłają pełny plik JAR przez sieć i ładują maszynę wirtualną do rozruchu.
  • Synchronizacja była bardzo obciążona (została zoptymalizowana dla każdej wersji Java). Refleksja jest jednak nadal kosztowna.
BobMcGee
źródło
49
Object instantiation and object-oriented features are blazing fast to use (faster than C++ in many cases) because they're designed in from the beginning.i Collections are fast. Standard Java beats standard C/C++ in this area, even for most optimized C code.są dzikimi roszczeniami nie popartymi żadnymi dowodami tutaj powiązanymi.
Sjoerd,
8
@ Sjoerd - Roszczenia nie są dzikie - są dla mnie oczywiste i powinny być dla każdego, kto rozumie różnice w architekturze domyślnego systemu pamięci w C / C ++ vs. Java. Państwo może zrobić dużo lepiej jeśli nadal pisać własne programy obsługi pamięci (z rzeczy, takich jak listy, wolnych pul pamięci, i tak dalej) lub korzystać z biblioteki, który implementuje takie.
Rex Kerr
15
@ Rex Kerr - Po co używać programów obsługi pamięci, jeśli można użyć np. Stosu do alokacji ?! Mylisz alokację pamięci sterty z tworzeniem instancji obiektu.
Sjoerd
20
@ Rex Kerr - Zasadniczo twierdzisz, że ponieważ wszystko w Javie wymaga alokacji pamięci na stercie, a ponieważ alokacja Javy na stercie w Javie jest szybsza niż w C ++, wszystko w Javie jest szybsze. Oto kilka wiadomości dla Ciebie: w C ++ w wielu przypadkach możesz zrobić to bez przydzielania pamięci na stos!
Sjoerd,
10
@ Sjoerd - Gdzie powiedziałem, że wszystko w Javie jest szybsze? Po prostu przeczytaj to, co powiedziałem. Powiedziałem, co mam na myśli, i zająłem się już wszystkim, co powiedziałeś w swoim ostatnim komentarzu.
Rex Kerr
49

Początkowo Java nie była szczególnie szybka, ale nie jest też zbyt wolna. Obecnie Java jest bardzo szybka. Od ludzi, z którymi rozmawiałem, wrażenie, że Java jest powolny, wynika z dwóch rzeczy:

  1. Wolny czas uruchamiania maszyny wirtualnej. Wczesna implementacja Java zajęła dużo czasu, aby uruchomić i załadować wymagane biblioteki i aplikację w porównaniu do aplikacji rodzimych.

  2. Powolny interfejs użytkownika. Early Swing był powolny. Prawdopodobnie nie pomogło to, że większość użytkowników systemu Windows uznała też domyślny Metal L&F za brzydki.

Biorąc pod uwagę powyższe punkty, nic dziwnego, że ludzie odnieśli wrażenie, że Java jest wolna.

Dla użytkowników lub programistów przyzwyczajonych do tworzenia aplikacji natywnych, a nawet aplikacji Visual Basic , te dwa punkty są najbardziej widoczne w aplikacji i jest to pierwsze wrażenie, jakie uzyskasz na temat aplikacji (chyba że jest to aplikacja inna niż GUI, w której przypadek dotyczy tylko 1.).

Nie przekonasz użytkownika, że ​​„wykonuje on kod bardzo szybko”, gdy aplikacja potrzebuje 8 sekund do uruchomienia w porównaniu ze swoją starą aplikacją Visual Basic, która uruchamia się natychmiast - nawet jeśli wykonanie kodu i czas uruchamiania mogą w ogóle nie być połączone.

Zrujnowanie pierwszego wrażenia to świetny sposób na rozpoczęcie plotek i mitów. Plotki i mity są trudne do zabicia.

Krótko mówiąc, Java nie jest wolna. Ludzie z nastawieniem „Java jest wolna” opierają się na pierwszych wrażeniach z Java ponad 10 lat temu.

nos
źródło
3
Kilka lat temu Java była bardzo wolna, ale w ostatnich testach porównawczych działa prawie tak szybko, jak C / C ++, aw niektórych sytuacjach działa szybciej.
ChadNC
23
Aplikacje Java w OSX 10.6 na moim Macbooku uruchamiają się znacznie wolniej niż aplikacje napisane w Objective-C. Jakie dowody na szybkie czasy uruchamiania?
Zan Lynx
2
Dekompresja absolutnie nie stanowi problemu z wydajnością. Mój komputer w 1992 roku zdekompresował pliki wykonywalne podczas uruchamiania programów, które poprawiły wydajność w porównaniu z ładowaniem dłuższego pliku z dysku twardego. Różnica między procesorem a dyskiem twardym ogromnie wzrosła w ciągu kolejnych lat. Występuje jednak problem z użyciem formatu archiwum zip dla pliku rt.jar (dlaczego? !!!), a zawarte w nim pliki klas nie są połączone (orzechy !!).
Tom Hawtin - tackline
5
@Zan: zwróć uwagę, że JVM dla Mac OS X jest napisany (lub przynajmniej dostosowany) przez Apple. Firma Sun zainwestowała sporo czasu, aby przyspieszyć uruchamianie na obsługiwanych platformach (Windows, Linux i Solaris), ale nie mogła tego zrobić w systemie Mac OS x (ponieważ nie utrzymują tego portu). Być może Mac nie mógł / nie zastosował / nie przeniósł wszystkich tych optymalizacji do Mac OS X.
Joachim Sauer
1
Nie uważam, aby Java była powolna (znam twórcę gier, który tworzy w nich gry); po prostu źle z powodów interfejsu użytkownika. Żadna „zwykła” aplikacja Java, którą widziałem, nie ma przyzwoitego, całkowicie działającego interfejsu użytkownika.
RCIX
40

Po przeczytaniu strony pełnej komentarzy, która mówi, że Java nie jest powolna, muszę tylko odpowiedzieć z odmienną opinią.

Powolność języka zależy w dużej mierze od oczekiwań dotyczących „szybkiego”. Jeśli uważasz, że C # jest szybki, Java z pewnością też jest szybka. Jeśli twoja domena problemowa jest związana z bazami danych lub przetwarzaniem w czasie rzeczywistym, Java jest z pewnością wystarczająco szybka. Jeśli z przyjemnością skalujesz swoją aplikację, dodając więcej sprzętu, Java prawdopodobnie jest dla Ciebie szybka. Jeśli uważasz, że stałe przyspieszanie czynników w skali 5-10 nie jest tego warte, prawdopodobnie rozważasz szybkie Java.

Jeśli wykonujesz obliczenia numeryczne na dużych zestawach danych lub jesteś związany ze środowiskiem wykonawczym, w którym zasoby procesora są ograniczone, gdzie stałe przyspieszenie w skali 5-10 byłoby ogromne. Nawet przyspieszenie o 0,5 może oznaczać skrócenie obliczeń o 500 godzin. W takich przypadkach Java po prostu nie pozwala uzyskać ostatniego poziomu wydajności i prawdopodobnie uważasz, że Java jest powolna.

Sami
źródło
2
zgodził się i daje +1 za cały post, ponieważ przedstawiasz prawidłowy punkt, jednak na przykład C ++ ma inną sławę, że jest trudny do debugowania i łatwo zdmuchnąć całą nogę, ale rzadko słyszałem, że C ++ jest tak powolny jak słyszałem o java.
Stefano Borini,
33

Wygląda na to, że zadajesz dwa dość różne pytania:

  1. Czy Java jest naprawdę wolna, a jeśli tak, to dlaczego?
  2. Dlaczego Java jest postrzegana jako wolna, mimo że jest szybsza niż wiele alternatyw?

Pierwsze z nich jest mniej więcej pytaniem „jak długa jest lina”. Wszystko sprowadza się do twojej definicji „wolnego”. W porównaniu do czystego interpretera, Java jest niezwykle szybka. W porównaniu do innych języków, które są (normalnie) kompilowane do jakiegoś kodu bajtowego, a następnie dynamicznie kompilowane do kodu maszynowego (np. C # lub cokolwiek innego w .NET) Java jest mniej więcej równa. W porównaniu z językami, które zwykle są kompilowane do czystego kodu maszynowego, i mają (często duże) zespoły ludzi pracujące nad niczym innym, jak tylko ulepszaniem swoich optymalizatorów (np. C, C ++, Fortran, Ada) Java radzi sobie w kilku sprawach, ale ogólnie bywa co najmniej nieco wolniejszy.

Wiele z tego dotyczy przede wszystkim implementacji - w zasadzie sprowadza się to do faktu, że użytkownik czeka na uruchomienie kompilatora dynamicznego / JIT, więc jeśli nie masz programu, który uruchamia się przez dłuższy czas, jest to trudno uzasadnić, że kompilator spędza dużo czasu na trudnych optymalizacjach. Dlatego większość kompilatorów Java (i C # itp.) Nie wkłada wiele wysiłku w naprawdę trudne optymalizacje. W wielu przypadkach mniej zależy od tego, jakie optymalizacje są wykonywane, niż od ich zastosowania. Wiele problemów związanych z optymalizacją zostało zakończonych NP, więc ich czas rośnie szybko wraz z wielkością atakowanego problemu. Jednym ze sposobów na utrzymanie czasu w granicach rozsądku jest zastosowanie optymalizacji do czegoś takiego jak pojedyncza funkcja na raz. Gdy tylko programista czeka na kompilator, możesz sobie pozwolić na dłuższe działanie i zastosować tę samą optymalizację do znacznie większych części programu. Podobnie, kod niektórych optymalizacji jest dość włochaty (i dlatego może być dość duży). Ponownie, ponieważ użytkownik czeka na załadowanie tego kodu (a czas uruchamiania JVM jest często znaczącym czynnikiem w całym czasie), implementacja musi zrównoważyć czas zaoszczędzony w jednym miejscu w porównaniu do utraconego w innym - i biorąc pod uwagę, jak mało kodu korzysta z owłosionych optymalizacji, utrzymywanie małej JVM jest zwykle bardziej korzystne.

Drugi problem polega na tym, że w Javie często pojawia się mniej więcej jedno rozwiązanie uniwersalne. Na przykład dla wielu programistów Java Swing jest zasadniczo jedyną dostępną biblioteką okienkową. W czymś takim jak C ++ istnieje dosłownie dziesiątki bibliotek okienkowych, struktur aplikacji itp., Każda z własnym zestawem kompromisów między łatwością użycia a szybkim wykonaniem, spójnym wyglądem i działaniem vs. rodzimym wyglądem i tak dalej. Jedynym faktem jest to, że niektóre (np. Qt) mogą być dość drogie (przynajmniej do użytku komercyjnego).

Po trzecie, dużo kodu napisanego w C ++ (a jeszcze bardziej C) jest po prostu starsze i bardziej dojrzałe. W większości zawiera rdzeń procedur napisanych kilkadziesiąt lat temu, gdy spędzanie dodatkowego czasu na optymalizacji kodu było normalnym, oczekiwanym zachowaniem. To często ma rzeczywistą zaletę w kodzie, który jest mniejszy i szybszy. C ++ (lub C) zasługuje na to, że kod jest mały i szybki, ale tak naprawdę jest to o wiele więcej produkt dewelopera i ograniczenia czasu pisania kodu. W pewnym stopniu prowadzi to do samospełniającej się przepowiedni - kiedy ludzie dbają o szybkość, często wybierają C ++, ponieważ ma tę reputację. Włożyli dodatkowy czas i wysiłek w optymalizację, a napisano nową generację szybkiego kodu C ++.

Podsumowując, normalna implementacja Java sprawia, że ​​maksymalna optymalizacja jest co najmniej problematyczna. Co gorsza, gdy Java jest widoczna , takie rzeczy jak okienkowanie zestawów narzędzi i czas uruchamiania JVM często odgrywają większą rolę niż szybkość wykonywania samego języka. W wielu przypadkach C i C ++ również doceniają to, co tak naprawdę jest wynikiem cięższej pracy przy optymalizacji.

Co do drugiego pytania, myślę, że w dużej mierze jest to kwestia ludzkiej natury w pracy. Kilku fanatyków twierdzi, że Java jest niesamowicie szybka. Ktoś go wypróbuje i stwierdzi, że uruchomienie nawet trywialnego programu zajmuje kilka sekund, a po uruchomieniu jest powolny i niezdarny. Niewielu prawdopodobnie przeszkadza w analizowaniu rzeczy, aby zdać sobie sprawę, że znaczną część tego stanowi czas uruchomienia JVM, oraz fakt, że kiedy po raz pierwszy wypróbowują rzeczy, żaden kod nie został jeszcze skompilowany - część kodu jest interpretowana, a niektóre są kompilowane podczas oczekiwania. Co gorsza, nawet jeśli działa wystarczająco szybko, wygląd i styl zwykle wydają się obce i niezdarne dla większości użytkowników, więc nawet jeśli obiektywne pomiary wykazały szybki czas reakcji, nadal wydawałoby się niezdarny.

Dodanie ich do siebie prowadzi do dość prostej i naturalnej reakcji: że Java jest powolna, brzydka i niezdarna. Biorąc pod uwagę szum mówiący, że jest naprawdę szybki, istnieje tendencja do przesadnej reakcji i wniosku, że jest to strasznie powolne, zamiast (dokładniejszego) „nieco wolniejszego, i to głównie w określonych okolicznościach”. Jest to na ogół najgorsze dla programisty piszącego kilka pierwszych programów w tym języku. Wykonanie programu „witaj świecie” w większości języków jest natychmiastowe, ale w Javie jest zauważalna przerwa podczas uruchamiania JVM. Nawet czysty interpreter, który działa znacznie wolniej na ciasnych pętlach i taki nadal często pojawia się szybciej w przypadku takiego kodu, po prostu dlatego, że można go załadować i zacząć wykonywać nieco wcześniej.

Jerry Coffin
źródło
16

To przestarzałe informacje z pierwszych dni (od połowy do końca lat 90.) Javy. Każda większa wersja Java wprowadza znaczne przyspieszenie w porównaniu do poprzedniej wersji. Ponieważ Oracle najwyraźniej łączy JRockit z JVM firmy Sun dla Java 7, trend ten będzie się utrzymywał.

W porównaniu z wieloma innymi popularnymi współczesnymi językami (Python, Ruby, PHP), Java jest w rzeczywistości znacznie szybsza dla większości zastosowań. Nie do końca pasuje do C lub C ++, ale dla wielu zadań jest wystarczająco blisko. Rzeczywiste obawy dotyczące wydajności powinny polegać na tym, ile pamięci zużyje.

Dan Dyer
źródło
14

Głównym winowajcą „długiego czasu uruchamiania” jest dynamiczne łączenie. Aplikacja Java składa się ze skompilowanych klas. Każda klasa odwołuje się do innych klas (dla typów argumentów, wywołań metod ...) według nazwy . JVM musi sprawdzić i dopasować te nazwy podczas uruchamiania. Robi to stopniowo, wykonując tylko te części, których potrzebuje w danym momencie, ale wciąż jest to trochę do zrobienia.

W aplikacji C ta faza łączenia występuje na końcu kompilacji. Jest wolny, szczególnie w przypadku dużych aplikacji, ale widzi go tylko programista. Łączenie daje plik wykonywalny, który system operacyjny musi po prostu załadować do pamięci RAM „tak jak jest”.

W Javie łączenie odbywa się za każdym razem, gdy aplikacja jest uruchamiana. Stąd długi czas uruchamiania.

Zastosowano różne optymalizacje, w tym techniki buforowania, a komputery stają się szybsze (i stają się „szybsze” niż aplikacje stają się „większe”), dlatego ostatnio znaczenie problemu znacznie się zmniejszyło; ale stare uprzedzenia pozostają.

Jeśli chodzi o wydajność później, moje własne testy porównawcze w obliczeniach kompaktowych z dostępem do macierzy (głównie funkcje skrótu i ​​inne algorytmy kryptograficzne) zwykle pokazują, że zoptymalizowany kod C jest około 3 razy szybszy niż kod Java; czasami C jest tylko 30% szybszy niż Java, czasami C może być 4x szybszy, w zależności od zaimplementowanego algorytmu. Widziałem 10-krotny współczynnik, gdy kod „C” był w rzeczywistości asemblerem dla dużej arytmetyki liczb całkowitych, ze względu na opcje mnożenia 64x64-> 128, które oferuje procesor, ale Java nie może użyć, ponieważ jego najdłuższym typem całkowitym jest 64-bit long. To jest przypadek na krawędzi. W warunkach praktycznych, I / O oraz względy przepustowości pamięci zapobiec kod C od bycia naprawdę trzy razy szybciej niż Java.

Thomas Pornin
źródło
Hmm ... Myślałem, że większość bibliotek C jest obecnie również dynamicznie połączonych. A może mówisz o czymś innym?
Sean McMillan,
4
@Sean: dynamiczne łączenie dla C występuje w przypadku „symboli zewnętrznych”: funkcji, które są używane w jednej bibliotece DLL, a zdefiniowane w innej. Typowa aplikacja C korzysta z kilkunastu bibliotek DLL. W przypadku Javy dynamiczne łączenie występuje dla wszystkich metod we wszystkich klasach: w typowej aplikacji Java (w tym w standardowej bibliotece) są tysiące takich metod . W świecie C większość połączeń (wszystkie łącza, które nie przekraczają granicy DLL) są rozwiązywane w czasie kompilacji, pozostała tylko niewielka część do wykonania w czasie wykonywania.
Thomas Pornin
14

Java jest zdecydowanie wolna, szczególnie w przypadku pracy ilościowej.

Używam kombinacji R , Python i C / C ++ ze zoptymalizowanymi wielowątkowymi bibliotekami ATLAS . W każdym z tych języków potrafię pomnożyć macierz 3000 na 3000 macierzy podwójnych ze sobą w około 4 sekundy. Korzystanie z Colt i Parallel Colt w Javie, ta sama operacja zajmuje 185 sekund! Zadziwiające, mimo że biblioteki java mają charakter równoległy.

Podsumowując, czysta Java nie nadaje się do pracy ilościowej. Jblas wydaje się być najlepszą biblioteką algebry liniowej dla Javy, ponieważ wykorzystuje ATLAS.

Moje urządzenie to HP Core 2 Duo z 3 GB pamięci RAM. Używam 64-bitowego systemu Ubuntu 10.04 (Lucid Lynx).

Hamaad Shah
źródło
Kontynuując mój komentarz, wykonałem tę samą operację mnożenia macierzy przy użyciu JAMA i zajęło to około 50 sekund. Wciąż zbyt wolno w porównaniu do innych języków.
Hamaad Shah
7
Ile czasu zajęła Java, kiedy wykonałeś mnożenie w bibliotekach wywoływanych przez JNI. Biorąc pod uwagę, że wszystko, co możesz zrobić w C / C ++, możesz zrobić z JNI (dodaj kilkaset mnano sekund), margines jest stosunkowo mały. Zgaduję, że twoje mnożenie macierzy R i Pythona nie zostało napisane w języku R lub Python, tylko wywołane z tych języków.
Peter Lawrey
2
Z ciekawości, czy zrobiłeś jakieś profilowanie, aby zidentyfikować, czy masz jakiś punkt aktywny w kodzie (konwersja typu / autoboxing)?
Thorbjørn Ravn Andersen
10

Dla większości ludzi doświadczenie w interakcji z nim - Java działa wolno. Wszyscy widzieliśmy, jak filiżanka kawy kręci się w przeglądarce, zanim pojawi się jakiś aplet. Rozbudowanie JVM i pobranie plików binarnych apletów zajmuje trochę czasu, co wpływa na wrażenia użytkownika w zauważalny sposób.

Nie pomaga to, że powolne uruchamianie JVM i pobieranie apletów jest wyraźnie oznakowane filiżanką kawy Java, więc ludzie kojarzą oczekiwanie z Javą. Gdy ładowanie Flash zajmuje dużo czasu, twórca Flash określa branding komunikatu „ładowanie”, więc ludzie nie obwiniają technologii Flash jako całości.

Wszystko to nie ma nic wspólnego z wydajnością Javy na serwerze ani na wiele innych sposobów, w jakie Java jest wykorzystywana poza przeglądarką. Ale to widzą ludzie i to, co pamiętają programiści spoza Javy, kiedy myślą o Javie.

Spike Williams
źródło
9

Java ma reputację powolności, ponieważ była powolna. Pierwsze wersje Javy nie zawierały kompilacji Just In Time, a raczej słabą. Oznaczało to, że kod, chociaż kod bajtowy, był interpretowany, więc nawet w przypadku najprostszych operacji (takich jak dodanie dwóch liczb całkowitych) maszyna musiała wykonywać wszelkiego rodzaju porównania, dereferencje wskaźnika i wywołania funkcji. Kompilator JIT jest ciągle ulepszany; teraz jest to moment, w którym jeśli niedbale piszę kod C ++, a kod Java niedbale, Java czasami osiąga lepsze wyniki niż C ++, ponieważ kompilator JIT zdaje sobie sprawę, że mam pewne niepotrzebne dereferencje wskaźników i zajmie się tym za mnie.

Jeśli chcesz zobaczyć, jak duża jest różnica w kompilacji JIT, sprawdź interpretowane i nieinterpretowane testy porównawcze w grze Computer Languages ​​Benchmark . (Pidigits korzysta z zewnętrznej biblioteki do wykonywania wszystkich obliczeń, aby test się nie zmienił; inne pokazują przyspieszenie 6-16x!)

To jest główny powód. Istnieje wiele innych, mniejszych powodów, które nie pomogły: początkowo czas uruchamiania Java był długi (teraz naprawiony); pobieranie aplikacji internetowych w Javie zajmuje dużo czasu (teraz jest to mniej prawdziwe w przypadku szeroko dostępnego łącza szerokopasmowego i oczekiwanych dużych rzeczy, takich jak filmy); Swing interfejsu użytkownika nie został (i nadal nie jest) napisany z myślą o wydajności, więc jest znacznie mniej zgryźliwy niż odpowiedniki np. w C ++.

Rex Kerr
źródło
6

Java działała powoli, z powrotem w ciągu dnia. Stało się znacznie szybsze, dzięki kilku generacjom ulepszeń wydajności . Ostatnio słyszałem, że zwykle mieści się w granicach 10% prędkości C # - czasami szybciej, czasem wolniej.

Uruchamianie apletów Java jest nadal wolne, ponieważ musisz uruchomić całą maszynę JVM, która musi załadować wszystkie swoje klasy. Trochę jak uruchamianie innego komputera. Po uruchomieniu JVM jest dość szybki, ale zwykle jest to, co ludzie pamiętają.

Ponadto jest co najmniej kilka osób , które nigdy nie uwierzą w żywotność Java.

Kaleb Brasee
źródło
1
Niestety uruchomienie JVM jest nadal znacznie wolniejsze niż uruchomienie CLR. Wynika to z faktu, że firma Sun w najgorszy sposób
postawiła czoła
3
Wow, Java 6 ma 4 lata ??? Tak, chyba tak (jeśli liczyć beta). Nadal wydaje mi się nowy - właśnie przestałem używać 1.4 w pracy.
Kaleb Brasee
Java 1.4 jest użyteczna, ale trochę sukkastyczna, ponieważ 1.5 i 1.6 dodały wiele ulepszeń wydajności i cukru syntaktycznego. Od tego czasu wprowadzono optymalizacje Bounds-check i System.arraycopy. Było też wiele ulepszeń synchronizacji. Myślę, że uczciwie jest powiedzieć, że 1.4 naprawdę jest powolne.
BobMcGee
LOL, wiem - za każdym razem, gdy muszę iterować ręcznie lub używać tablicy zamiast ogólnej listy, chcę rozbić laptopa na pół ... IBM rzeczywiście od lat ma Java 5 na WAS 6.1, ale „ utknąłem na WAS 6.0 :( Używam Java 5/6, odkąd wyszedł na moje potrzeby, ale jestem ograniczony przez stare wersje serwerowe w pracy. Istnieją dwucyfrowe procentowe ulepszenia wydajności od 1.4 do najnowszych wersji na wiele rzeczy i nie mogę się ich doczekać
Kaleb Brasee
6

Stefano:

Jestem z Javą od samego początku, więc z mojego punktu widzenia sława bycia powolnym została stworzona przez niereagujące i powolne nakładki GUI (AWT, a następnie Swing) oraz w apletach prawdopodobnie ze względu na dodatkowe wolne czasy uruchamiania VM.

Java przewidziała i promowała wiele badań w obszarze VM, a także wprowadzono pewne ulepszenia, w tym wyrzucanie elementów bezużytecznych (w rzeczywistości można dostroić wiele rzeczy; jednak często widzę systemy, w których używane są tylko wartości domyślne) i hotspot optymalizacja (która na początku i prawdopodobnie nadal jest bardziej wydajna po stronie serwera).

Java na zapleczu i na poziomie obliczeniowym nie jest tak wolna. Colt jest jednym z najlepszych przykładów:

Najnowsza stabilna wersja Colta przełamuje barierę 1,9 Gflop / s na JDK ibm-1.4.1, RedHat 9.0, 2x IntelXeon@2,8 GHz.

Istnieje wiele rzeczy poza głównym nurtem Java, które należy wziąć pod uwagę, takie jak Realtime Java lub specjalne mechanizmy zwiększające prędkość, takie jak Javolution , a także kompilacja Ahead-Of-Time (jak gcj). Istnieją również układy scalone, które mogą bezpośrednio wykonywać Java Bytecode, jak na przykład ten, który jest w obecnych iPhone'ach i odtwarzaczach iPod ARM Jazelle .

Myślę, że ogólnie dzisiaj jest to decyzja polityczna (jak brak obsługi Javy na iPhonie / iPodzie) i decyzja przeciwko Javie jako językowi (ponieważ wielu uważa, że ​​jest zbyt gadatliwa).

Istnieje jednak wiele innych języków dla maszyny wirtualnej Java (np. Python, Ruby, JavaScript, Groovy, Scala itp.), Które mogą być alternatywą.

Osobiście nadal cieszę się z tego, że jest to elastyczna i niezawodna platforma z doskonałą dostępnością narzędzi i bibliotek, która pozwala pracować ze wszystkim, od najmniejszego urządzenia (np. JavaCard) po największe serwery.

Dieter
źródło
Ok, więc kolejna zła reputacja pochodzi z zestawu narzędzi GUI. Oczywiście zakładam, że ponieważ współczesne JVM używają natywnych widgetów, łączą się z bibliotekami systemu operacyjnego, prawda? czy używają AWT / Swing, aby renderować ten sam wygląd platformy hosta?
Stefano Borini,
Stefano: Swing opiera się na idei nienatywnego renderowania uniwersalnego widgetów, więc twoje założenie jest trochę błędne. Jest to rzeczywiście mechanizm „wtykowy wygląd”, który pozwala komponentom Swing naśladować wygląd komponentów natywnych. Jeśli szukasz czegoś takiego, możesz wypróbować SWT ( eclipse.org/swt ), który rzeczywiście zaczepi się w natywnym systemie operacyjnym i użyje natywnych widżetów za pomocą JNI (o czym mówi się, że jest wąskim gardłem).
Dieter
Java2D (używane w Swing) jest obecnie bardzo szybkie, a używanie natywnych widżetów (SWT) nie zapewnia żadnej poprawy wydajności. Przynajmniej tak czytam, kiedy 6 miesięcy temu decydowałem, czy nauczyć się Swing, czy SWT.
Luigi Plinge 17.10.11
4

Młotek jest znacznie wolniejszy przy rozwałkowaniu ciasta niż wiele innych narzędzi. Nie sprawia, że ​​młot jest „wolniejszy”, ani mniej przydatny do zadań, do których jest przeznaczony.

Jako ogólny język programowania, Java jest na równi z wieloma (jeśli nie większością) dla szerokiego zakresu zadań programistycznych. Istnieją specyficzne, trywialne testy, w których Java nie będzie lepsza od ręcznie kodowanych rozwiązań w mniej wyrafinowanych językach, takich, które są „bliższe metalu”.

Ale jeśli chodzi o „aplikacje z prawdziwego świata”, Java często jest właściwym narzędziem. To powiedziawszy, nic nie powstrzyma programistów przed stworzeniem wolno działającego rozwiązania przy użyciu DOWOLNEGO narzędzia. Niewłaściwe użycie narzędzia jest dobrze znanym problemem (wystarczy spojrzeć na reputację PHP i VB). Jednak (przeważnie) czysty design i składnia Javy robi wiele, aby ograniczyć niewłaściwe użycie.

mobiGeek
źródło
3

Java jest językiem wysokiego poziomu, a obecnie jego reputacją jest wydajność porównywalna z innymi porównywalnymi językami wysokiego poziomu.

  1. Ma dynamiczną semantykę wiązania . W porównaniu do C ++, w którym metody inne niż wirtualne są kompilowane jako wywołania funkcji, nawet najlepszy kompilator Java na świecie musi wytwarzać mniej wydajny kod. Ale jest to również czystsza, bardziej znacząca semantyczna.

  2. Nie pamiętam szczegółów, ale słyszałem we wczesnych dniach Javy, że dla każdego obiektu Java istnieje muteks, który można zdobyć i wydać każdą metodą. To sprawia, że ​​lepiej dostosowuje się do współbieżności, chociaż niestety tylko muteks na obiekt nie ochroni cię przed wyścigami, impasami lub innymi złymi rzeczami, które mogą się zdarzyć w współbieżnych programach. Ta część, jeśli jest prawdziwa, jest trochę naiwna, ale wynikała z dobrych intencji. Jeśli znasz więcej na ten temat, możesz podać mi szczegółowe informacje.

  3. Innym sposobem, w jaki Java jest językiem wysokiego poziomu, jest Garbage-Collection . Garbage Collection może być wolniejsze niż malloci freeprogramów, które przeznaczają na raz całej pamięci potrzebują i pracy z tym. Problem polega na tym, że w językach, które nie mają funkcji Garbage-Collection, programiści mają tendencję do pisania tylko programów, które przydzielają całą potrzebną pamięć na raz i kończą się niepowodzeniem, jeśli okaże się, że została przekroczona dowolna stała maksymalnego rozmiaru. Tak więc porównanie to jabłka do pomarańczy. Gdy programiści starają się pisać i debugować programy z dynamicznym przydzielaniem struktur łańcuchowych w językach innych niż GC, czasami stwierdzają, że ich programy nie są już szybsze niż w języku GC, ponieważ mallocifreenie są darmowe! Mają też narzuty ... Poza tym brak GC wymusza określenie, kto co uwolni, a także określenie, kto zwalnia, co z kolei zmusza cię do robienia kopii - gdy kilka funkcji będzie potrzebowało danych i nie jest jasne, które będzie go używać na końcu - podczas gdy kopiowanie nie byłoby konieczne w języku GC.

Pascal Cuoq
źródło
1. Prawdopodobnie nieprawda z HotSpot. 2. Tylko jeśli oznaczysz metodę jako zsynchronizowaną.
Winston Ewert,
1
1. Kompilator nie optymalizuje kodu, ale JVM jest wystarczająco inteligentny, aby dynamicznie określać, że zwykle wywoływana jest tylko jedna lub dwie metody wirtualne i można je wywoływać statycznie, a nawet wstawiać. Jestem prawie pewien, że C ++ nie może wbudować wirtualnych metod. 2. Każdy obiekt Java ma blokadę. Ma mały narzut (około bajtu) na każdy obiekt, ale nie ma większego wpływu, jeśli nie zostanie użyty. 3. W Javie możesz przydzielić jednocześnie wszystkie potrzebne obiekty. Może to dać Ci aplikację, która nie GC przez cały dzień. ;) GC Java jest domyślnie wielowątkowe, co wymaga specjalnych bibliotek w C ++.
Peter Lawrey
C ++ może wbudowywać wirtualne wywołania, ale Java może to robić w większej liczbie przypadków, a także jest silniejsze dzięki optymalizacji megamorficznych witryn z połączeniami.
Piotr Kołaczkowski
2

W połowie lat dziewięćdziesiątych, kiedy Java pojawiła się w głównym nurcie, C ++ był dominującym językiem, a sieć była wciąż dość nowa. Ponadto JVM i GC były stosunkowo nowymi koncepcjami w głównym nurcie rozwoju. Wczesne maszyny JVM były dość powolne (w porównaniu do C ++ działającego na gołym metalu), a także cierpiały z powodu długich przerw na śmiecie, co spowodowało, że Java była powolna.

Ken Liu
źródło
czy było to spowodowane technologią stojącą za GC? Wiem, że mają pewne strategie (takie jak warstwy generacyjne dla obiektów), które mają być bardziej wydajne w fazie GC. Jaka była strategia w tym czasie?
Stefano Borini,
1
Ekspert IANA JVM, ale myślę, że w tym czasie w GC był używany jeden wątkowy algorytm mark / sweep, który wymagał zatrzymania całej JVM podczas wykonywania GC. Obecnie istnieje równoległa ocena / zamiatanie, a także istnieje wiele innych ulepszeń wydajności w JVM.
Ken Liu
2
Nowoczesne algorytmy GC są ładne, ale myślę, że największą poprawą był JIT.
Pascal Thivent
1

Wiele aplikacji Java (obecnie: Eclipse) ma złą reakcję GUI, prawdopodobnie ze względu na duże zużycie pamięci i fakt, że moduł ładujący klasy może wykonywać wiele operacji IO. Poprawia się, ale było gorzej kilka lat temu.

Wiele (większość) osób lubi uogólniać, więc mówią „Java jest wolna”, ponieważ widzą, że aplikacje działają wolno podczas interakcji z nimi.

Wojciech Kaczmarek
źródło
czy uważasz, że wysokie zużycie pamięci pochodzi z narzędzia lub z bibliotek Java?
Stefano Borini
W przypadku Eclipse - z samej infrastruktury Eclipse. To samo dotyczy „ciężkich” GUI w przeszłości (JBuilder, jak pamiętam). Mam przeczucie, że to dlatego, że potrzeba wielu obiektów typu „płyta podstawowa”, aby używać architektury wtyczek (takich jak Eclipse) w języku o typie statycznym. Emacs ma również wtyczki, a jego zużycie pamięci jest 10-20 razy mniejsze niż Eclipse podczas typowego kodowania. Kod Emacs Lisp jest kompilowany do kodu bajtowego i ładowany do instancji emacs, a następnie uruchamiany - podobnie jak program ładujący klasy Java. Wydaje mi się, że w Javie jest mnóstwo instancji obiektów pośrednich, które pozwalają na pewne wtyczki.
Wojciech Kaczmarek
1

Głównym problemem z aplikacjami Java jest to, że jest ogromny ze względu na duży rozmiar podstawowej biblioteki wykonawczej. Ogromne programy wypełniają dużo pamięci i mają tendencję do zamiany, co oznacza, że ​​stają się wolne.

Powodem, dla którego Sun JVM jest duży, jest to, że ma bardzo dobry interpreter kodu bajtowego, który działa, śledząc wiele rzeczy. To oznacza dużo danych, co oznacza pamięć.

Możesz spojrzeć na maszynę wirtualną jamvm, która jest dość szybkim interpretatorem (bez kodu natywnego) i bardzo małym. Zaczyna się nawet szybko.

Thorbjørn Ravn Andersen
źródło
1

Jak mówi Pascal, Java jest na równi z innymi językami wysokiego poziomu. Jednak jako ktoś, kto pracował z oryginalnymi maszynami JVM w systemie Windows 98 , poziom abstrakcji zapewniany przez maszynę wirtualną Java był, powiedzmy, bolesny.

Zasadniczo była to emulacja oprogramowania z niewielką lub żadną optymalizacją, którą dzisiaj przyjmujemy za pewnik w JVM.

beczułka
źródło
0

Ludzie zwykle kłusują po linii „to jest interpretowane”. Ponieważ kiedyś tak było, a zła prasa jest przekazywana przez ludzi, którzy zrzucili Javę jako „zbyt wolno” i nigdy nie powrócili, aby przetestować nowsze wersje.

A może lepszym rozwiązaniem jest „ludzie są idiotami”.

Mr. Boy
źródło
0

Myślę, że któregoś dnia, być może nie w niedalekiej przyszłości, języki skompilowane w JIT będą pod każdym względem lepsze niż języki skompilowane (no, może nie czas uruchamiania / zużycie pamięci) ze względu na fakt, że kompilatory JIT mogą intensywnie wykorzystywać środowisko uruchomieniowe zachowanie i platforma, na której działają.

helpermethod
źródło
6
Myślę, że masz na myśli to, że skompilowany (nie zinterpretowany) kod JIT pobije kod AoT. Interpretacja zawsze będzie wolniejsza niż uruchomienie skompilowanego kodu. Dlatego używane są kompilatory JIT. Złap: istnieje niewielka różnica między kompilatorem wyprzedzającym a kompilatorem JIT pod względem wydajności, z tym wyjątkiem, że kompilator JIT musi kompilować się szybciej i może korzystać z informacji o środowisku wykonawczym, aby wskazać swoje optymalizacje. Kompilatory AoT specyficzne dla platformy z optymalizacjami specyficznymi dla platformy prawie zawsze pokonają JIT, ponieważ nie ma ograniczenia, ile czasu poświęcają na optymalizację.
BobMcGee
Dzięki za odpowiedź, nigdy nie myślałem o krótkim czasie, jaki mają do dyspozycji kompilatory JIT.
helpermethod
masz na myśli coś takiego jak optymalizacja adaptacyjna hotspot?
Stefano Borini,
2
@BobMcGee: Bardzo dobrze. Program C ++ można zbudować tak, aby działał wolno z informacją zwrotną z profilu dla wszystkich swoich operacji. Następnie kompilator może odbudować bardzo szybką wersję, wykorzystując pół godziny czasu procesora i 2 GB pamięci RAM. JIT, który to zrobił, sprawiłby, że użytkownicy odeszli.
Zan Lynx,
1
Czas kompilacji JIT jest nieistotny w przypadku długo działających aplikacji, takich jak serwery. AOT z PGO jest bardziej ograniczony w porównaniu do JIT z co najmniej 2 powodów. Po pierwsze, największą różnicę wydajności uzyskuje się dzięki lekkim optymalizacjom. Porównaj gcc -O2 z gcc -O3. Przez większość czasu nie ma różnicy , czasami -O3 może być nieco lepszy, czasem nieco gorszy, ale nigdy nie doświadczyłem różnicy> 2x. Po drugie, używając AOT nawet z PGO, możesz tylko zgadywać, jaki profil będzie na stronie użytkowania. Zgadnij źle, a ty jesteś daleko w tyle za JIT. Rzeczywisty profil może być bardzo zależny od konfiguracji.
Piotr Kołaczkowski