Załóżmy, że program został napisany w dwóch różnych językach, niech to będą język X i język Y, jeśli ich kompilatory generują ten sam kod bajtowy, dlaczego powinienem używać języka X zamiast języka Y? Co określa, że jeden język jest szybszy od drugiego?
Pytam o to, ponieważ często zdarza się, że ludzie mówią takie rzeczy: „C jest najszybszym językiem, ATS jest językiem szybkim jak C”. Chciałem zrozumieć definicję „szybkiego” dla języków programowania.
programming-languages
compilers
Rodrigo Valente
źródło
źródło
Odpowiedzi:
Istnieje wiele powodów, dla których można rozważyć wybór języka X zamiast języka Y. Czytelność programu, łatwość programowania, przenośność na wiele platform, istnienie dobrych środowisk programowania może być takimi przyczynami. Zastanowię się jednak tylko nad szybkością wykonania, zgodnie z żądaniem zawartym w pytaniu. Pytanie wydaje się nie uwzględniać na przykład tempa rozwoju.
Dwa języki mogą się kompilować do tego samego kodu bajtowego, ale nie oznacza to, że zostanie wygenerowany ten sam kod,
W rzeczywistości bytecode jest kodem tylko dla konkretnej maszyny wirtualnej. Ma zalety inżynieryjne, ale nie wprowadza zasadniczych różnic w bezpośredniej kompilacji dla określonego harware. Równie dobrze możesz rozważyć porównanie dwóch języków skompilowanych do bezpośredniego wykonania na tym samym komputerze.
To powiedziawszy, kwestia względnej szybkości języków jest stara i sięga pierwszych kompilatorów.
Przez wiele lat w tamtych czasach profesjonalista uważał, że odręczny kod jest szybszy niż kod skompilowany. Innymi słowy, język maszynowy był uważany za szybszy niż języki wysokiego poziomu, takie jak Cobol lub Fortran. I było zarówno szybsze, jak i zwykle mniejsze. Języki wysokiego poziomu wciąż się rozwijały, ponieważ były znacznie łatwiejsze w użyciu dla wielu osób, które nie były informatykami. Koszt użycia języków wysokiego poziomu miał nawet nazwę: współczynnik rozszerzenia, który może dotyczyć wielkości generowanego kodu (bardzo ważna kwestia w tamtych czasach) lub liczby faktycznie wykonanych instrukcji. Pomysł był głównie eksperymentalny, ale początkowo stosunek był większy niż 1, ponieważ kompilatory wykonały dość prostą robotę według dzisiejszych standardów.
W ten sposób język maszynowy był szybszy niż powiedzieć Fortran.
Oczywiście zmieniło się to z biegiem lat, gdy kompilatory stały się bardziej wyrafinowane, do tego stopnia, że programowanie w asemblerze jest teraz bardzo rzadkie. W przypadku większości aplikacji programy w asemblerze słabo konkurują z kodem generowanym przez optymalizację kompilatorów.
To pokazuje, że jednym z głównych problemów jest jakość kompilatorów dostępnych dla rozważanego języka, ich zdolność do analizy kodu źródłowego i odpowiedniej jego optymalizacji.
Ta zdolność może zależeć w pewnym stopniu od cech języka, aby podkreślić strukturalne i matematyczne właściwości źródła, aby ułatwić pracę kompilatorowi. Na przykład język może pozwalać na dołączanie instrukcji o właściwościach algebraicznych funkcji zdefiniowanych przez użytkownika, aby umożliwić kompilatorowi wykorzystanie tych właściwości do celów optymalizacji.
Proces kompilacji może być łatwiejszy, dzięki czemu powstaje lepszy kod, gdy paradygmat programowania języka jest bliższy funkcjom maszyn, które zinterpretują kod, zarówno rzeczywistej, jak i wirtualnej.
Inną kwestią jest to, czy paradygmaty zaimplementowane w języku są zamknięte dla rodzaju programowanego problemu. Należy się spodziewać, że język programowania wyspecjalizowany w określonych paradygmatach programowania będzie bardzo skutecznie kompilował funkcje związane z tym paradygmatem. Dlatego wybór języka programowania może zależeć, dla jasności i szybkości, od wyboru języka programowania dostosowanego do rodzaju programowanego problemu.
Popularność C w programowaniu systemu wynika prawdopodobnie z faktu, że C jest zbliżony do architektury maszyny, a programowanie systemu jest również bezpośrednio związane z tą architekturą.
Niektóre inne problemy będą łatwiejsze do zaprogramowania, dzięki szybszemu wykonaniu przy użyciu programowania logiki i języków rozwiązywania ograniczeń .
Złożone systemy reaktywne mogą być bardzo skutecznie programowane za pomocą specjalistycznych synchronicznych języków programowania, takich jak Esterel, który zawiera bardzo specjalistyczną wiedzę na temat takich systemów i generuje bardzo szybki kod.
Lub, na przykład, niektóre języki są wysoce wyspecjalizowane, takie jak języki opisu składni używane do programowania parserów. Parser generator to nic innego jak kompilator dla tych języków. Oczywiście nie jest to kompletna metoda Turinga, ale te kompilatory są wyjątkowo dobre ze względu na swoją specjalizację: tworzenie wydajnych programów parsujących. Ponieważ dziedzina wiedzy jest ograniczona, techniki optymalizacji mogą być bardzo wyspecjalizowane i bardzo dokładnie dostrojone. Te generatory parsera są zwykle znacznie lepsze niż to, co można uzyskać, pisząc kod w innym języku. Istnieje wiele wysoce wyspecjalizowanych języków z kompilatorami, które wytwarzają doskonały i szybki kod dla ograniczonej klasy problemów.
Dlatego przy pisaniu dużego systemu może być wskazane, aby nie polegać na jednym języku, ale wybrać najlepszy język dla różnych składników systemu. To oczywiście rodzi problemy z kompatybilnością.
Inną kwestią, która często ma znaczenie, jest po prostu istnienie wydajnych bibliotek dla programowanych tematów.
Wreszcie, szybkość nie jest jedynym kryterium i może być w konflikcie z innymi kryteriami, takimi jak bezpieczeństwo kodu (na przykład w odniesieniu do złych danych wejściowych lub odporność na błędy systemowe), użycie pamięci, łatwość programowania (chociaż zgodność paradygmatu może w rzeczywistości pomóc ), rozmiar kodu obiektu, łatwość konserwacji programu itp.
Prędkość nie zawsze jest najważniejszym parametrem. Może również przybierać różne formy, na przykład złożoność, która może być średnią złożonością lub gorszą złożonością przypadku. Ponadto w dużym systemie, podobnie jak w mniejszym programie, są części, w których prędkość ma kluczowe znaczenie, i inne, w których nie ma to większego znaczenia. I nie zawsze łatwo jest to ustalić z góry.
źródło
Chociaż wszystko ostatecznie działa na procesorze * , istnieją różne różnice między różnymi językami. Oto kilka przykładów.
Języki interpretowane Niektóre języki są interpretowane, a nie kompilowane , na przykład Python, Ruby i Matlab. Oznacza to, że kod Pythona i Ruby nie kompiluje się z kodem maszynowym, ale jest interpretowany w locie. Możliwe jest skompilowanie Pythona i Ruby na maszynie wirtualnej (patrz następny punkt). Zobacz także to pytanie . Z różnych powodów interpretowany kod jest ogólnie wolniejszy niż skompilowany kod. Sama interpretacja może być nie tylko powolna, ale także trudniejsze do optymalizacji. Jeśli jednak Twój kod spędza większość czasu na funkcjach bibliotecznych (przypadek Matlaba), wydajność nie ucierpi.
Maszyna wirtualna Niektóre języki są kompilowane do kodu bajtowego , wynalezionego „kodu maszynowego”, który jest następnie interpretowany. Typowymi przykładami są Java i C #. Podczas gdy kod bajtowy może być konwertowany w locie na kod maszynowy, kod prawdopodobnie nadal będzie działał wolniej. W przypadku Java do przenoszenia używana jest maszyna wirtualna. W przypadku C # mogą istnieć inne obawy, takie jak bezpieczeństwo.
Koszty ogólne Niektóre języki wymieniają się wydajnością dla bezpieczeństwa. Na przykład niektóre wersje Pascala sprawdzałyby, że nie masz dostępu do tablicy poza granicami. Kod C # jest „zarządzany”, a to kosztuje. Innym częstym przykładem jest wyrzucanie elementów bezużytecznych, które oszczędza czas programistom, ale nie jest tak wydajne, jak praktyczne zarządzanie pamięcią. Istnieją inne źródła narzutów, takie jak infrastruktura do obsługi wyjątków lub do wspierania programowania obiektowego.
* W rzeczywistości dzisiejsze systemy o wysokiej wydajności również uruchamiają kod na GPU, a nawet na FPGA.
źródło
Istnieją różne czynniki wyboru X zamiast Y, jak
Niektóre języki są odpowiednie do tworzenia projektów biznesowych, takich jak C # lub Python, ale z drugiej strony niektóre z nich są dobre do programowania systemu, takiego jak C ++.
Musisz określić, na jakiej platformie będziesz pracować i jaką aplikację zamierzasz stworzyć.
źródło
„Najszybszym” językiem programowania, jaki można uzyskać na dowolnej platformie, jest język asemblera chipsetu, z którym mamy do czynienia. Na tym poziomie nie ma tłumaczeń. Jednak trzeba mieć pewną wiedzę o tym, jak chipset wykonuje instrukcje, szczególnie te, które mogą robić rzeczy równolegle.
Konwersja z C do zestawu jest bardzo „płytka”, ponieważ jest bliska jeden do jednego, ale jest bardziej czytelna. Jednak ma wiele warstw nad nim ze względu na standardowe biblioteki w celu poprawy przenośności. Kompilator nie musi robić tak wielu rzeczy, aby dostać się do kodu asemblera, a generalnie istnieją silniejsze optymalizacje, aby wprowadzić zmiany specyficzne dla maszyny.
C ++ dodaje bogatszy język. Jednak ponieważ język dodaje tyle złożoności, kompilatorowi trudniej jest stworzyć optymalny kod dla platformy.
Następnie przechodzimy na drugą stronę skali. Języki interpretowane. Zwykle są one najwolniejsze, ponieważ oprócz wykonania pracy jest trochę czasu na parsowanie kodu i przekształcenie go w wywołania maszynowe.
Następnie mamy te pomiędzy. Zasadniczo mają warstwę maszyny wirtualnej zoptymalizowaną dla platformy. A kompilator utworzy kod do uruchomienia maszyny wirtualnej. Czasami dzieje się to od razu jak Perl, Pascal, Ruby lub Python. Lub w kilku etapach, takich jak Java.
Niektóre z tych maszyn wirtualnych dodają pojęcie kompilatora JIT, który przyspiesza również środowisko wykonawcze poprzez tworzenie kodu na poziomie maszyny zamiast tłumaczenia kodu bajtu pośredniego.
Niektóre maszyny wirtualne mają niski poziom, co pozwala na mniejszą translację kodu bajtowego na kod maszynowy. Które przyspieszają, zachowując przenośność.
źródło
*p++=*q++;
na wielu komputerach byłby szybszy niż,array1[i]=array2[i];
ale na wielu procesorach odwrotność jest często prawdziwa, a zatem kompilatory mogą w końcu przekonwertować poprzedni styl kodu na drugi - nie jest to „płytka” konwersja.-O0
, nie dokona żadnych optymalizacji. Optymalizacje to premia, którą można uzyskać dzięki kompilatorowi, ale sam język może tłumaczyć od jednego do jednego do asemblera.Nie wspomniałem jeszcze o tym, że w niektórych językach uruchomienie tego samego fragmentu kodu wielokrotnie wykona tę samą sekwencję działań; komputer musi więc tylko raz ustalić, co powinna zrobić sekcja kodu. Jedną z głównych zalet dialektu JavaScript „używaj ściśle” jest to, że gdy silnik JavaScript zorientuje się, co robi fragment kodu, może wykorzystać te informacje przy następnym uruchomieniu; bez „ścisłego użycia” nie może.
Na przykład przy braku „ścisłego użycia” fragment kodu taki jak:
może zwrócić zmienną X w kontekście bezpośredniego wywołania, jeśli istnieje, lub zmienną X z zewnętrznego kontekstu wywołania, lub może zwrócić
Undefined
. Gorzej, w pętli takiej jak:silnik JavaScript nie
g()
może wiedzieć, co może z tym zrobići
[lubg
samemu sobie. Ponieważg
lubi
może całkiem słusznie zmienići
się w ciąg, silnik JavaScript nie może po prostu użyć dodawania liczbowego i porównania numerycznego w pętli, ale musi przy każdym przejściu przez pętlę sprawdzać, czy którekolwiek z wywołań funkcji coś zrobiłoi
lubg
. Natomiast w dialekcie „użyj ścisłego” [nieco rozsądnego] silnik JavaScipt może zbadać powyższy kod i wiedzieć, że każde przejście przez pętlę będzie używać tej samej zmiennej liczbowej i wywoływać tę samą funkcję. Będzie zatem musiał jedynie zidentyfikowaći
i funkcjonowaćg
raz, zamiast konieczności sprawdzania ich przy każdym przejściu przez pętlę - duża oszczędność czasu.źródło
Cóż, są tu dość profesjonalne odpowiedzi, ta nie jest im bliska, ale może być dla ciebie intuicyjna.
Być może słyszałeś wiele razy, że gdy chcesz wykonać zadanie tak szybko, jak to możliwe, chciałbyś napisać kod, który wykonuje go w asemblerze. Wynika to z faktu, że wykonujesz tylko te polecenia, które są rzeczywiście potrzebne do wykonania zadania i nic więcej. Chociaż w języku wysokiego poziomu możesz zaimplementować to zadanie w kilku wierszach, kompilator nadal musi je przetłumaczyć na język maszynowy. To tłumaczenie nie zawsze jest minimalistyczne, ponieważ można je napisać bezpośrednio. Oznacza to, że maszyna wyda wiele zegarów na wykonywanie poleceń, które można oszczędzić.
Chociaż kompilatory są dziś bardzo wyrafinowane, nadal nie są skuteczne, jak mogą być najlepsi programiści.
Kontynuując w tym kierunku, te niepotrzebne polecenia rosną w swojej ilości (zwykle), gdy język jest wyższy poziom. (nie dotyczy to w 100% wszystkich języków wysokiego poziomu)
Więc dla mnie język X jest szybszy niż język Y (w środowisku wykonawczym), jeśli dla określonego fragmentu kodu kod maszynowy X jest krótszy niż Y.
źródło
Trudno jest ostatecznie odpowiedzieć na to pytanie, ponieważ jest ono tak złożone i wielowymiarowe (jest prawie jak np. Porównywanie marek samochodów z różnymi kryteriami), ale istnieją nowe badania naukowe, w tym doskonałe repozytorium kodów znane jako kod Rosetta ( przegląd Wikipedii ). W ankiecie Nanz i Furii z 2014 r. Badano to pytanie dość definitywnie i naukowo w oparciu o następujące typowe kryteria i rzadką analizę ilościową typowo subiektywnych cech kodu. Streszczenie zawiera pewne obiektywnie uzasadnione ustalenia i uogólnienia. (Mam nadzieję, że w przyszłości zostaną przeprowadzone inne badania oparte na tych wynikach).
RQ1. Które języki programowania zapewniają bardziej zwięzły kod?
RQ2. Które języki programowania kompilują się w mniejsze pliki wykonywalne?
RQ3. Które języki programowania mają lepszą wydajność w czasie wykonywania?
RQ4. Które języki programowania wydajniej wykorzystują pamięć?
RQ5. Które języki programowania są mniej podatne na awarie?
źródło
Języki komputerowe to tylko abstrakcja poleceń wyjaśniających komputerowi, co robić.
Możesz nawet pisać w języku komputerowym
Python
i kompilować go za pomocą kompilatora C (cython).Mając to na uwadze, nie można porównywać szybkości języków komputerowych.
Ale do pewnego stopnia możesz porównywać kompilatory dla tego samego języka. Na przykład
GNU C
kompilator kontraIntel C
kompilator. (Wyszukaj test kompilatora)źródło