Przejrzałem Google i przeszukałem witrynę Go, ale nie mogę znaleźć wytłumaczenia dla niezwykłych czasów kompilacji Go. Czy są to produkty funkcji językowych (lub ich brak), wysoce zoptymalizowany kompilator, czy coś innego? Nie próbuję promować Go; Jestem po prostu ciekawy.
performance
compiler-construction
build-process
go
Evan Kroske
źródło
źródło
Odpowiedzi:
Analiza zależności.
Go FAQ wykorzystywane zawierać następujące zdanie:
Chociaż tego wyrażenia nie ma już w FAQ, ten temat jest rozwinięty w wykładzie Go w Google , który porównuje podejście do analizy zależności C / C ++ i Go.
To jest główny powód szybkiej kompilacji. I to jest z założenia.
źródło
Myślę, że to nie jest tak, że kompilatory Go są szybkie , tylko że inne kompilatory działają wolno .
Kompilatory C i C ++ muszą analizować ogromne ilości nagłówków - na przykład kompilacja „hello world” C ++ wymaga skompilowania 18 000 wierszy kodu, co stanowi prawie pół megabajta źródeł!
Kompilatory Java i C # działają na maszynie wirtualnej, co oznacza, że zanim cokolwiek będą mogły kompilować, system operacyjny musi załadować całą maszynę wirtualną, a następnie muszą zostać skompilowane z kodu bajtowego na kod macierzysty, co zajmuje trochę czasu.
Szybkość kompilacji zależy od kilku czynników.
Niektóre języki są zaprojektowane do szybkiej kompilacji. Na przykład Pascal został zaprojektowany do kompilacji przy użyciu kompilatora jednoprzebiegowego.
Same kompilatory można również zoptymalizować. Na przykład kompilator Turbo Pascal został napisany w asemblerze zoptymalizowanym ręcznie, co w połączeniu z projektem językowym zaowocowało naprawdę szybkim kompilatorem działającym na sprzęcie klasy 286. Myślę, że nawet teraz nowoczesne kompilatory Pascal (np. FreePascal) są szybsze niż kompilatory Go.
źródło
Istnieje wiele powodów, dla których kompilator Go jest znacznie szybszy niż większość kompilatorów C / C ++:
Główny powód : większość kompilatorów C / C ++ wykazuje wyjątkowo złe projekty (z perspektywy prędkości kompilacji). Ponadto, z perspektywy prędkości kompilacji, niektóre części ekosystemu C / C ++ (takie jak edytory, w których programiści piszą swoje kody) nie są zaprojektowane z myślą o szybkości kompilacji.
Główny powód : Szybka kompilacja była świadomym wyborem w kompilatorze Go, a także w języku Go
Kompilator Go ma prostszy optymalizator niż kompilatory C / C ++
W przeciwieństwie do C ++ Go nie ma szablonów ani funkcji wbudowanych. Oznacza to, że Go nie musi wykonywać żadnej instancji szablonu ani funkcji.
Kompilator Go generuje kod asemblera wcześniej, a optymalizator działa na kod asemblera, podczas gdy w typowym kompilatorze C / C ++ optymalizacja przekazuje pracę na wewnętrznej reprezentacji oryginalnego kodu źródłowego. Dodatkowy narzut w kompilatorze C / C ++ wynika z faktu, że należy wygenerować wewnętrzną reprezentację.
Końcowe linkowanie (5l / 6l / 8l) programu Go może być wolniejsze niż linkowanie programu C / C ++, ponieważ kompilator Go przechodzi przez cały użyty kod asemblera i może wykonuje także inne dodatkowe czynności niż C / C ++ linkery nie działają
Niektóre kompilatory C / C ++ (GCC) generują instrukcje w formie tekstowej (przekazywane do asemblera), podczas gdy kompilator Go generuje instrukcje w formie binarnej. Należy wykonać dodatkową pracę (ale niewiele), aby przekształcić tekst w plik binarny.
Kompilator Go jest ukierunkowany tylko na niewielką liczbę architektur CPU, podczas gdy kompilator GCC jest ukierunkowany na dużą liczbę procesorów
Kompilatory zaprojektowane z myślą o wysokiej prędkości kompilacji, takie jak Jikes, są szybkie. Na procesorze 2GHz Jikes może skompilować ponad 20000 linii kodu Java na sekundę (a przyrostowy tryb kompilacji jest jeszcze bardziej wydajny).
źródło
Wydajność kompilacji była głównym celem projektu:
Często zadawane pytania dotyczące języka są dość interesujące w odniesieniu do określonych funkcji językowych związanych z analizą:
źródło
aType
to było odwołanie do zmiennej, a później w fazie analizy semantycznej, kiedy okazuje się, że nie drukujesz znaczącego błędu w tym czasie.Chociaż większość powyższych stwierdzeń jest prawdziwa, istnieje jedna bardzo ważna kwestia, o której tak naprawdę nie wspomniano: zarządzanie zależnościami.
Go musi tylko dołączyć pakiety, które importujesz bezpośrednio (ponieważ te już zaimportowały to, czego potrzebują). Jest to wyraźny kontrast w stosunku do C / C ++, gdzie każdy pojedynczy plik zaczyna się od x nagłówków, które zawierają y nagłówki itp. Konkluzja: Kompilacja Go zajmuje liniowy czas wrt do liczby importowanych pakietów, gdzie C / C ++ zajmuje wykładniczy czas.
źródło
Dobrym sprawdzianem wydajności tłumaczenia kompilatora jest samokompilacja: jak długo kompiluje się dany kompilator? W przypadku C ++ zajmuje to bardzo dużo czasu (godziny?). Dla porównania, kompilator Pascal / Modula-2 / Oberon by opracować się w ciągu mniej niż jednej sekundy na nowoczesne maszyny [1].
Języki Go zostały zainspirowane tymi językami, ale niektóre z głównych przyczyn tej wydajności obejmują:
Jasno zdefiniowana składnia, która jest matematycznie poprawna, dla wydajnego skanowania i analizowania.
Bezpieczny i statycznie skompilowany język, który wykorzystuje osobną kompilację z kontrolą zależności i typu między granicami modułów, aby uniknąć niepotrzebnego ponownego odczytu plików nagłówkowych i ponownej kompilacji innych modułów - w przeciwieństwie do niezależnej kompilacji, takiej jak w C / C ++, gdzie kompilator nie przeprowadza takich kontroli między modułami (stąd potrzeba ponownego czytania wszystkich plików nagłówkowych w kółko, nawet w przypadku prostego, jednoliniowego programu „hello world”).
Wydajna implementacja kompilatora (np. Parsowanie z góry na dół w trybie pojedynczego przejścia, z rekurencyjnym opadaniem) - co oczywiście w dużym stopniu pomaga punkt 1 i 2 powyżej.
Zasady te były już znane i w pełni wdrożone w latach 70. i 80. XX wieku w językach takich jak Mesa, Ada, Modula-2 / Oberon i kilka innych, a dopiero teraz (w 2010 r.) Trafiają do nowoczesnych języków, takich jak Go (Google) , Swift (Apple), C # (Microsoft) i kilka innych.
Miejmy nadzieję, że wkrótce będzie to norma, a nie wyjątek. Aby się tam dostać, muszą się zdarzyć dwie rzeczy:
Po pierwsze, dostawcy platform oprogramowania, tacy jak Google, Microsoft i Apple, powinni zacząć od zachęcania twórców aplikacji do korzystania z nowej metodologii kompilacji, jednocześnie umożliwiając im ponowne wykorzystanie istniejącej bazy kodu. Właśnie to Apple próbuje teraz zrobić z językiem programowania Swift, który może współistnieć z Objective-C (ponieważ używa tego samego środowiska wykonawczego).
Po drugie, same platformy oprogramowania powinny ostatecznie zostać z czasem przepisane przy użyciu tych zasad, jednocześnie przeprojektowując hierarchię modułów w tym procesie, aby stały się mniej monolityczne. Jest to oczywiście ogromne zadanie i może zająć większą część dekady (jeśli są wystarczająco odważni, by to zrobić - czego wcale nie jestem pewien w przypadku Google).
W każdym razie to platforma napędza adopcję języka, a nie na odwrót.
Bibliografia:
[1] http://www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf , strona 6: „Kompilator kompiluje się w około 3 sekundy”. Ten cytat dotyczy taniej płytki rozwojowej FPGA Xilinx Spartan-3 działającej na częstotliwości taktowania 25 MHz i posiadającej 1 MB pamięci głównej. Z tego można łatwo ekstrapolować na „mniej niż 1 sekundę” dla nowoczesnego procesora pracującego na częstotliwości taktowania znacznie powyżej 1 GHz i kilku GB głównej pamięci (tj. Kilka rzędów wielkości większej mocy niż karta FPGA Xilinx Spartan-3), nawet biorąc pod uwagę prędkości we / wy. Już w 1990 r., Kiedy Oberon był uruchomiony na procesorze NS32X32 25 MHz z 2-4 MB pamięci głównej, kompilator skompilował się w kilka sekund. Pojęcie faktycznego czekaniakompilator kończący cykl kompilacji był zupełnie nieznany programistom Oberona już wtedy. W przypadku typowych programów usuwanie palca z przycisku myszy, który wywołał polecenie kompilacji , zawsze trwało dłużej niż oczekiwanie na zakończenie kompilacji. To była naprawdę natychmiastowa satysfakcja z niemal zerowym czasem oczekiwania. A jakość wytworzonego kodu, choć nie zawsze w pełni porównywalna z najlepszymi dostępnymi wówczas kompilatorami, była wyjątkowo dobra dla większości zadań i ogólnie akceptowalna.
źródło
Go został zaprojektowany, aby być szybki i to pokazuje.
Pamiętaj, że GO nie jest jedynym językiem z takimi funkcjami (moduły są normą w nowoczesnych językach), ale zrobiły to dobrze.
źródło
Cytując z książki „ The Go Programming Language ” Alana Donovana i Briana Kernighana:
źródło
Podstawowa idea kompilacji jest w rzeczywistości bardzo prosta. Parser rekurencyjno-opadający w zasadzie może działać z prędkością związaną z I / O. Generowanie kodu jest w zasadzie bardzo prostym procesem. Tablica symboli i podstawowy system typów nie wymagają wiele obliczeń.
Spowolnienie kompilatora nie jest jednak trudne.
Jeśli istnieje faza preprocesor, z wielopoziomowego obejmują dyrektyw, definicje makr i kompilacja warunkowa, tak przydatne, jak te rzeczy są, to nie jest trudne, aby załadować go w dół. (Na przykład myślę o plikach nagłówkowych Windows i MFC.) Dlatego właśnie prekompilowane nagłówki są potrzebne.
Jeśli chodzi o optymalizację generowanego kodu, nie ma ograniczeń co do ilości przetwarzania, które można dodać do tej fazy.
źródło
Po prostu (własnymi słowami), ponieważ składnia jest bardzo łatwa (do analizy i parsowania)
Na przykład brak dziedziczenia typu oznacza bezproblemową analizę, aby dowiedzieć się, czy nowy typ jest zgodny z regułami narzuconymi przez typ podstawowy.
Na przykład w tym przykładzie kodu: „interfejsy” kompilator nie idzie i sprawdza, czy zamierzony typ implementuje dany interfejs podczas analizy tego typu. Tylko do momentu użycia (i JEŻELI zostanie użyty) kontrola jest wykonywana.
Innym przykładem jest kompilator informujący, czy deklarujesz zmienną i jej nie używasz (lub czy powinieneś zachować wartość zwracaną, a nie jesteś)
Następujące elementy się nie kompilują:
Tego rodzaju egzekwowania i zasady sprawiają, że powstały kod jest bezpieczniejszy, a kompilator nie musi wykonywać dodatkowych weryfikacji, które może zrobić programista.
Zasadniczo wszystkie te szczegóły ułatwiają analizowanie języka, co powoduje szybkie kompilacje.
Ponownie, własnymi słowami.
źródło
myślę, że Go został zaprojektowany równolegle z tworzeniem kompilatora, więc byli najlepszymi przyjaciółmi od urodzenia. (IMO)
źródło
Co jeszcze?
źródło