Czasami Java przewyższa C ++ w testach porównawczych. Oczywiście czasami C ++ osiąga lepsze wyniki.
Zobacz następujące linki:
- http://keithlea.com/javabench/
- http://blog.dhananjaynene.com/2008/07/performance-comparison-c-java-python-ruby-jython-jruby-groovy/
- http://blog.cfelde.com/2010/06/c-vs-java-performance/
Ale jak to w ogóle możliwe? Dziwi mnie, że interpretacja kodu bajtowego może być szybsza niż skompilowany język.
Czy ktoś może wyjaśnić? Dzięki!
java
c++
performance
Deets McGeets
źródło
źródło
Odpowiedzi:
Po pierwsze, większość maszyn JVM zawiera kompilator, więc „interpretowany kod bajtowy” jest w rzeczywistości dość rzadki (przynajmniej w kodzie testowym - nie jest tak rzadki w prawdziwym życiu, w którym Twój kod to zwykle więcej niż kilka trywialnych pętli, które bardzo często się powtarzają ).
Po drugie, spora liczba odnośnych testów porównawczych wydaje się dość tendencyjna (czy to z intencji, czy z niekompetencji, naprawdę nie mogę powiedzieć). Na przykład przed laty patrzyłem na część kodu źródłowego połączonego z jednym z opublikowanych linków. Miał taki kod:
Ponieważ
calloc
zapewnia pamięć, która jest już wyzerowana,for
ponowne użycie pętli do zera jest oczywiście bezużyteczne. Następnie (jeśli pamięć służy) wypełnianie pamięci i tak innymi danymi (i brak zależności od wyzerowania), więc i tak całe zerowanie było całkowicie niepotrzebne. Zastąpienie powyższego kodu prostąmalloc
(jak każda rozsądna osoba na początku) poprawiła szybkość wersji C ++ na tyle, aby pokonać wersję Java (o dość szeroki margines, jeśli pamięć służy).Rozważ (na inny przykład)
methcall
test porównawczy użyty we wpisie na blogu w ostatnim linku. Pomimo nazwy (i tego, jak mogłoby to nawet wyglądać), wersja tego C ++ tak naprawdę wcale nie mierzy dużo narzutu wywołania metody. Część kodu, która okazuje się być krytyczna, znajduje się w klasie Toggle:Kluczową częścią okazuje się
state = !state;
. Zastanów się, co się stanie, gdy zmienimy kod na kodowanie stanuint
zamiastbool
:Ta niewielka zmiana poprawia ogólną prędkość o około 5: 1 margines . Mimo, że benchmark był przeznaczony do pomiaru czasu metoda połączenia, w rzeczywistości większość tego, co było pomiaru był czas na konwersję pomiędzy
int
ibool
. Z pewnością zgodziłbym się z tym, że nieefektywność wykazana przez oryginał jest niefortunna - ale biorąc pod uwagę, jak rzadko wydaje się występować w prawdziwym kodzie, oraz łatwość, z jaką można to naprawić, kiedy / jeśli się pojawi, trudno jest mi myśleć tego, co wiele znaczy.W przypadku, gdy ktoś zdecyduje się ponownie uruchomić odnośne testy porównawcze, powinienem również dodać, że istnieje prawie równie trywialna modyfikacja wersji Java, która produkuje (lub przynajmniej raz wyprodukowała - nie uruchomiłem ponownie testów z ostatnie JVM, aby potwierdzić, że nadal tak robią), dość znacząca poprawa również w wersji Java. Wersja Java ma NthToggle :: Activate (), która wygląda następująco:
Zmiana tej opcji na wywołanie funkcji podstawowej zamiast
this.state
bezpośredniego manipulowania daje dość znaczną poprawę prędkości (choć nie wystarcza, aby nadążyć za zmodyfikowaną wersją C ++).W efekcie powstaje fałszywe założenie dotyczące interpretowanych kodów bajtów w porównaniu do niektórych najgorszych testów porównawczych (jakie kiedykolwiek widziałem). Ani też nie daje znaczącego wyniku.
Moje własne doświadczenie jest takie, że przy równie doświadczonych programistach, którzy zwracają jednakową uwagę na optymalizację, C ++ będzie częściej bić Javę - ale (przynajmniej między tymi dwoma) język rzadko robi tak dużą różnicę, jak programiści i projekt. Cytowane testy porównawcze mówią nam więcej o (nie) kompetencjach / (nie) uczciwości ich autorów niż o językach, które zamierzają przeprowadzać.
[Edycja: Jak sugerowano w jednym miejscu powyżej, ale nigdy nie podano tak bezpośrednio, jak prawdopodobnie powinienem, cytuję wyniki, które otrzymałem, kiedy testowałem to ~ 5 lat temu, używając implementacji C ++ i Java, które były aktualne w tym czasie . Nie uruchomiłem ponownie testów z bieżącymi implementacjami. Rzut oka wskazuje jednak, że kod nie został naprawiony, więc wszystko, co by się zmieniło, to zdolność kompilatora do ukrywania problemów w kodzie.]
Jeśli jednak zignorujemy przykłady Java, w rzeczywistości jest możliwe, że interpretowany kod działa szybciej niż kod skompilowany (choć trudny i nieco nietypowy).
Zwykle dzieje się tak, gdy interpretowany kod jest znacznie bardziej zwarty niż kod maszynowy lub działa na procesorze, który ma większą pamięć podręczną danych niż pamięć podręczna kodu.
W takim przypadku mały interpreter (np. Wewnętrzny interpreter implementacji Fortha) może całkowicie zmieścić się w pamięci podręcznej kodu, a program, który interpretuje, mieści się całkowicie w pamięci podręcznej danych. Pamięć podręczna jest zwykle szybsza niż pamięć główna dziesięciokrotnie, a często znacznie więcej (współczynnik 100 nie jest już szczególnie rzadki).
Jeśli więc pamięć podręczna jest szybsza niż pamięć główna o współczynnik N, a do wdrożenia każdego kodu bajtu potrzeba mniej niż N instrukcji kodu maszynowego, kod bajtów powinien wygrać (upraszczam, ale myślę, że ogólna idea powinna nadal być widocznym).
źródło
Ręcznie walcowane C / C ++ wykonane przez eksperta z nieograniczonym czasem będzie co najmniej tak szybkie lub szybsze niż Java. Ostatecznie sama Java jest napisana w C / C ++, więc możesz oczywiście zrobić wszystko, co robi Java, jeśli chcesz włożyć wystarczająco dużo wysiłku inżynieryjnego.
W praktyce jednak Java często wykonuje się bardzo szybko z następujących powodów:
Jednocześnie C / C ++ mają również pewne zalety:
Ogólny:
źródło
Środowisko wykonawcze Java nie interpretuje kodu bajtowego. Zamiast tego wykorzystuje tak zwaną kompilację Just In Time . Zasadniczo, gdy program jest uruchomiony, pobiera kod bajtowy i konwertuje go na kod natywny zoptymalizowany dla konkretnego procesora.
źródło
Wszystkie rzeczy są równe, można powiedzieć: nie, Java nigdy nie powinna być szybsza . Zawsze możesz zaimplementować Javę w C ++ od podstaw, a tym samym uzyskać co najmniej tak dobrą wydajność. W praktyce jednak:
Nawiasem mówiąc, przypomina mi to debatę dotyczącą C w latach 80. / 90. Wszyscy zastanawiali się „czy C może być tak szybki jak zgromadzenie?”. Zasadniczo odpowiedź brzmiała: nie na papierze, ale w rzeczywistości kompilator C stworzył wydajniejszy kod niż 90% programistów asemblerowych (cóż, kiedy już trochę dojrzał).
źródło
http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html
źródło
Podczas gdy całkowicie zoptymalizowany program Java rzadko pokonuje całkowicie zoptymalizowany program C ++, różnice w takich rzeczach jak zarządzanie pamięcią mogą sprawić, że wiele algorytmów jest idiomatycznie zaimplementowanych w Javie szybciej niż te same algorytmy idiomatycznie zaimplementowane w C ++.
Jak zauważył @Jerry Coffin, istnieje wiele przypadków, w których proste zmiany mogą znacznie przyspieszyć kod - ale często może to wymagać zbyt wiele nieczystych poprawek w jednym lub drugim języku, aby poprawa wydajności była opłacalna. To prawdopodobnie zobaczysz w dobrym teście porównawczym, który pokazuje, że Java radzi sobie lepiej niż C ++.
Ponadto, choć zwykle nie jest to aż tak znaczące, istnieje pewna optymalizacja wydajności, której język JIT, taki jak Java, może zrobić, czego nie potrafi C ++. Środowisko wykonawcze Java może zawierać ulepszenia po skompilowaniu kodu, co oznacza, że JIT może potencjalnie wygenerować zoptymalizowany kod, aby skorzystać z nowych (lub przynajmniej różnych) funkcji procesora. Z tego powodu 10-letni plik binarny Java może potencjalnie przewyższyć 10-letni plik binarny C ++.
Wreszcie, pełne bezpieczeństwo typu na szerszym obrazie może, w bardzo rzadkich przypadkach, zaoferować ekstremalną poprawę wydajności. Singularity , eksperymentalny system operacyjny napisany prawie całkowicie w języku C #, ma znacznie szybszą komunikację międzyprocesową i wielozadaniowość ze względu na fakt, że nie ma potrzeby granicznych procesów sprzętowych ani kosztownych przełączników kontekstowych.
źródło
Wysłane przez Tim Holloway na JavaRanch:
Źródło: http://www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time
źródło
Wynika to z faktu, że ostatni etap generowania kodu maszynowego odbywa się transparentnie wewnątrz JVM podczas uruchamiania programu Java, zamiast jawnego podczas budowania proramu C ++.
Należy wziąć pod uwagę fakt, że współczesne maszyny JVM spędzają sporo czasu na kompilowaniu kodu bajtowego w locie do natywnego kodu maszynowego, aby uczynić go tak szybko, jak to możliwe. Dzięki temu JVM może wykonywać wszelkiego rodzaju sztuczki kompilatora, które mogą być jeszcze lepsze, znając dane profilowania uruchomionego programu.
Właśnie taka rzecz, jak automatyczne wstawianie gettera, aby JUMP-RETURN nie był potrzebny do uzyskania wartości, przyspiesza rzeczy.
Jednak rzeczą, która naprawdę pozwoliła na szybkie programy, jest lepsze późniejsze czyszczenie. Mechanizm wyrzucania elementów bezużytecznych w Javie jest szybszy niż ręczny bez malloc w C. Wiele współczesnych implementacji wolnych od malloc korzysta ze śmieci pod spodem.
źródło
Krótka odpowiedź - nie jest. Zapomnij, temat jest tak stary jak ogień lub koło. Java lub .NET nie jest i nie będzie szybszy niż C / C ++. Jest wystarczająco szybki do większości zadań, w których nie trzeba w ogóle myśleć o optymalizacji. Podobnie jak formularze i przetwarzanie SQL, ale na tym się kończy.
W przypadku testów porównawczych lub małych aplikacji napisanych przez niekompetentnych programistów tak, końcowy rezultat będzie taki, że Java / .NET prawdopodobnie będzie blisko, a może nawet szybciej.
W rzeczywistości proste rzeczy, takie jak przydzielanie pamięci na stosie lub po prostu używanie stref pamięci, po prostu zabijają Java / .NET na miejscu.
Śmieciowy świat używa rodzaju memów z całą księgowością. Dodaj strefę C do C, a C będzie tam szybciej. Zwłaszcza dla testów porównawczych „wysokowydajnego kodu” Java vs. C, które wyglądają następująco:
Spróbuj użyć zmiennych opartych na stosie w C / C ++ (lub umieszczenie nowego), tłumaczą się na
sub esp, 0xff
to, jest to pojedyncza instrukcja x86, pobij to w Javie - nie możesz ...Przez większość czasu widzę te ławy, w których porównuje się Javę z C ++, co powoduje, że zaczynam, wth? Niewłaściwe strategie alokacji pamięci, samorosnące pojemniki bez rezerw, wiele nowości. Nie jest to nawet bliskie zorientowanemu na wydajność kodowi C / C ++.
Również dobra lektura: https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf
źródło
W rzeczywistości są to tylko asemblery wysokiego poziomu, które robią dokładnie to, co prosi programista, dokładnie tak, jak prosi programista w dokładnie takiej kolejności, w jakiej prosi programista. Różnice w wydajności są tak małe, że są nieistotne dla wszystkich praktycznych celów.
Język nie jest „wolny”, programista napisał wolny program. Bardzo rzadko program napisany w najlepszy sposób w jednym języku poza (w jakimkolwiek praktycznym celu) program robi to samo przy użyciu najlepszego sposobu w języku alternatywnym, chyba że autor badania zamierza zmielić swój topór.
Oczywiście, jeśli wybierasz się na rzadki przypadek, taki jak twarde systemy osadzone w czasie rzeczywistym, wybór języka może mieć znaczenie, ale jak często tak jest? a w tych przypadkach, jak często właściwy wybór nie jest ślepo oczywisty.
źródło
Keith Lea mówi ci, że są „oczywiste wady”, ale nie robi nic z tymi „oczywistymi wadami”. W 2005 r. Te stare zadania zostały odrzucone i zastąpione zadaniami wyświetlanymi teraz w grze porównawczej .
Keith Lea mówi ci, że „wziął kod porównawczy dla C ++ i Java z przestarzałej Great Computer Language Shootout i przeprowadził testy”, ale w rzeczywistości pokazuje tylko pomiary dla 14 z 25 tych nieaktualnych testów .
Keith Lea mówi teraz, że nie próbował niczego udowodnić postem na blogu siedem lat wcześniej, ale wtedy powiedział: „Miałem dość słuchania, jak ludzie mówią, że Java jest wolna, kiedy wiem, że jest dość szybka ...”, co sugeruje wtedy było coś, co próbował udowodnić.
Christian Felde mówi ci: „Nie stworzyłem kodu, po prostu ponownie uruchomiłem testy”. jakby to zwolniło go z wszelkiej odpowiedzialności za decyzję o opublikowaniu pomiarów zadań i programów wybranych przez Keitha Leę.
Czy pomiary nawet 25 maleńkich programów dostarczają ostatecznych dowodów?
Pomiary te są dla programów uruchamianych jako „tryb mieszany” Java nie interpretuje Java - „Pamiętam, jak działa HotSpot” Możesz łatwo dowiedzieć się, jak dobrze Java uruchamia „interpretowany kod bajtowy”, ponieważ możesz zmusić Javę do interpretowania tylko kodu bajtowego - wystarczy, że niektóre programy Java uruchomią się z opcją -Xint i bez niej.
źródło
Bawi mnie, jak wszechobecne jest to dziwne pojęcie „interpretowanego kodu bajtowego”. Czy słyszeliście kiedyś o kompilacji JIT? Twojego argumentu nie można zastosować do Javy.
Ale pomijając JVM, zdarzają się przypadki, gdy bezpośredni kod wątkowy lub nawet trywialna interpretacja kodu bajtowego może łatwo przewyższyć mocno zoptymalizowany kod macierzysty. Wyjaśnienie jest dość proste: kod bajtowy może być dość kompaktowy i zmieści się w twojej małej pamięci podręcznej, gdy natywna wersja kodu tego samego algorytmu skończy się z kilkoma brakami pamięci podręcznej dla jednej iteracji.
źródło
Poza JIT, GC i tak dalej, C ++ może być bardzo, bardzo łatwo spowolniony o wiele bardziej niż Java. Nie pojawi się to w testach porównawczych, ale ta sama aplikacja napisana przez programistę Java i programistę C ++ może być znacznie szybsza w Javie.
Jeśli chodzi o zaawansowane wzorce dziedziczenia, są one prawie do siebie podobne - C ++ ma takie, których Java nie ma i odwrotnie, ale wszystkie wprowadzają podobny, znaczny narzut. Więc nie ma żadnej specjalnej korzyści C ++ w programowaniu z dużym obciążeniem obiektowym.
Jeszcze jedno zastrzeżenie: GC może być szybsze lub wolniejsze niż ręczne zarządzanie przydziałami. Jeśli przydzielisz wiele małych obiektów, w środowisku GC zwykle przydzielana jest część pamięci i wysyłane są jej fragmenty w razie potrzeby dla nowych obiektów. W zarządzaniu - każdy obiekt = oddzielny przydział zajmuje dużo czasu. OTOH, jeśli malloc () dużo pamięci naraz, a następnie po prostu ręcznie przypisujesz jej część do swoich obiektów lub używasz kilku większych instancji obiektów, możesz znaleźć je znacznie szybciej.
źródło
obj.fetchFromDatabase("key")
trzy razy w ciągu pięciu wierszy kodu dla tego samego klucza, dwa razy zastanów się, czy pobrać tę wartość raz i zapisać ją w pamięci lokalnej. Jeśli piszeszobj->"key"
z->
przeciążeniem, by działać jako pobieranie bazy danych, jesteś o wiele bardziej podatny na to, by pozwolić mu przejść, ponieważ koszt operacji nie jest widoczny.W jakiś sposób Stack Exchange nie bierze moich innych punktów stosu, więc ... niestety niestety ...
Jednak moim zdaniem druga najczęściej głosowana odpowiedź jest pełna dezinformacji.
Ręcznie rozwijana aplikacja eksperta w C / C ++ ZAWSZE będzie znacznie szybsza niż aplikacja Java, kropka. Nie ma „tak szybkiej jak Java lub Faster”. jest po prostu szybszy, właśnie dzięki przedmiotom, które cytujesz poniżej:
Kompilacja JIT : czy naprawdę spodziewasz się, że automatyczny optymalizator będzie miał mądry programista i zobaczy związek między intencją a kodem, który procesor naprawdę uruchomi ??? Co więcej, wszystko, co robisz, to stracony czas w porównaniu do już skompilowanego programu.
Garbage Collection to narzędzie, które po prostu zwalnia zasoby, których programista zapomniałby zwolnić, w sposób mniej lub bardziej wydajny.
Oczywiście może to być tylko wolniejsze niż to, co zrobiłby ekspert (wybrałeś termin) programista C, aby poradzić sobie z pamięcią (i nie ma przecieków w poprawnie napisanych aplikacjach).
Zoptymalizowana pod kątem wydajności aplikacja C zna procesor, na którym jest uruchomiona, została na niej skompilowana, w przeciwnym razie oznacza to, że nie wykonałeś wszystkich kroków w celu zwiększenia wydajności, prawda?
Statystyka środowiska wykonawczego Jest to poza moją wiedzą, ale podejrzewam, że ekspert w języku C ma więcej niż wystarczającą wiedzę na temat prognozowania gałęzi, aby ponownie przechytrzyć automatyczną optymalizację -
Bardzo dobre biblioteki Istnieje wiele niezoptymalizowanych funkcji, które są łatwo dostępne przez biblioteki w Javie, i to samo dotyczy każdego języka, jednak najbardziej zoptymalizowane biblioteki są napisane w C, szczególnie do obliczeń.
JVM to warstwa abstrakcji, która implikuje zarówno dobre rzeczy, z których wiele jest powyżej, a także sugeruje, że ogólne rozwiązanie jest wolniejsze z założenia.
Ogólny:
Java nigdy nie osiągnie prędkości C / C ++ ze względu na sposób, w jaki działa w JVM z dużą ilością ochrony, funkcji i narzędzi.
C ++ ma wyraźną przewagę w zoptymalizowanym oprogramowaniu, czy to dla komputerów czy gier, i często można zobaczyć, że implementacje C ++ wygrywają konkursy kodowania do tego stopnia, że najlepsze implementacje Java można zobaczyć tylko na drugiej stronie.
W praktyce C ++ nie jest zabawką i nie pozwoli ci uniknąć wielu błędów, z którymi może sobie poradzić większość współczesnych języków, jednak będąc prostszym i mniej bezpiecznym, jest z natury szybszy.
Podsumowując, chciałbym powiedzieć, że większość ludzi nie daje za to dwóch centów, że w końcu optymalizacja jest sportem zarezerwowanym tylko dla bardzo niewielu szczęśliwych twórców i że z wyjątkiem przypadków, w których wydajność naprawdę stanowi problem (Tj. gdzie pomnożenie sprzętu przez 10 nie pomoże - lub reprezentuje co najmniej kilka milionów), większość menedżerów woli niezoptymalizowaną aplikację i masę sprzętu.
źródło
new
lubmalloc()
. Ogólnie może być znacznie szybsze niż jakiekolwiek ręczne zarządzanie pamięcią - ponieważ nie można ręcznie przenieść obiektów. Całe twoje rozumowanie jest po prostu błędne i stronnicze. Twoja wiedza na temat algorytmów GC i metod optymalizacji JIT jest zbyt ograniczona.Widziałem co najmniej dwa imponujące mmo wykonane w Javie, co oznacza, że nie jest wystarczająco szybki do gier, jest błędem. To, że twórcy gier wolą C ++ bardziej niż inne języki, mówi, że nie jest to związane tylko z Javą, oznacza to, że programiści nigdy tak naprawdę nie zajmowali się innymi językami / paradygmatami programowania. Wszystko w dowolnym języku tak zaawansowanym, jak C / C ++, a nawet Java, może wygenerować kod, który technicznie może spełnić lub pokonać argument prędkości. Wszystko, co zostało powiedziane, sprowadza się do tego, co wiedzą programiści, z jakimi zespołami współpracuje większość i co najważniejsze, dlaczego używają wspomnianych narzędzi. Ponieważ zajmujemy się aspektem programowania gier, argumentem musi być coś więcej. Po prostu to powiedz chodzi przede wszystkim o pieniądze i czas dla firmy, która jest martwa na używanie narzędzi, które spełniają wymogi kontroli jakości, aw realnym świecie nie ma żadnego znaczenia z powodu xx powodów wyboru C ++ zamiast Java lub innego języka. To tylko decyzja o masowej produkcji. Na najbardziej podstawowym poziomie algorytmów obliczeniowych, w których bawimy się jedynymi i zerami, argument prędkości jest jednym z najgłupszych argumentów, jakie kiedykolwiek stosowano w grach. Jeśli chcesz tak bardzo przyspieszyć, porzuć całkowicie języki programowania i pracuj z asemblerem, który jest zdecydowanie najlepszą zaletą.
źródło