2009-02-23 12 views

Odpowiedz

1

Oto strzał ..

Zastosowanie przyrostowe kompilacja jeśli toolchain je obsługuje. (marka, studio graficzne itp.).

Na przykład, w GCC/make, jeśli masz wiele plików do kompilacji, ale wprowadzasz tylko zmiany w jednym pliku, kompilowany jest tylko jeden plik.

+0

O ile nie jest to plik nagłówkowy. Często warto wkładać trochę wysiłku w faktorowanie plików .h, aby nie zmieniały się zbyt często. Jeden starszy projekt niepotrzebnie przechowuje dane w pliku nagłówkowym, który jest używany wszędzie. Nie ma tam przyrostowych zysków kompilacji. – Edmund

+0

Uh, myślę, że pliki nagłówkowe nie zostały skompilowane .. Podobnie, jeśli spróbujesz skompilować plik nagłówkowy w visual C++, pojawi się komunikat "Brak narzędzia kompilacji powiązanego z tym rozszerzeniem." W każdym razie, to prawda, ale tylko dla C/C++, może .. – krebstar

3

W jaki sposób mogę zminimalizować czas kompilacji?

  • Nie kompilacja (interpretowany język)
  • Opóźnione (just in time) kompilacja
  • przyrostowe kompilacja
  • prekompilowanego pliki nagłówkowe
+0

Zrobiliśmy test tutaj z VS6 kilka lat temu, a nasz duży projekt z 15 minutowym czasem kompilacji trwało około 6 sekund * dłużej * do skompilowania z prekompilowanym nagłówkiem pliki włączone. –

+0

Teraz, gdy patrzę na tę odpowiedź, mam również problemy z twoimi innymi pociskami. Głównym problemem jest to, że nie skracają one czasu kompilacji, lecz przenoszą go w inne miejsce. To * może * być tym, czego potrzebuje, ale nie jest to cielesne, o co prosił. –

+0

Używając VS6 IMO nie możesz sobie pozwolić na użycie "auto" dla prekompilowanego nagłówka: zamiast tego musisz dokładnie określić, który plik * .cpp je wygeneruje (np. "Stdafx.cpp") i ile prekompilowanych jest nagłówków. – ChrisW

0

to zależy od języka/platformy you” ponowne programowanie dla. dla rozwoju .NET, zminimalizuj liczbę projektów, które masz w swoim rozwiązaniu.

0

W dawnych czasach można uzyskać dramatyczne przyspieszenia, konfigurując napęd RAM i tam kompilując. Nie wiem, czy to nadal jest prawdą.

+0

Wspomniałem o tym także w mojej odpowiedzi. Jestem też ciekawy, czy ktoś nadal to robi. –

0

W C++ można użyć rozproszonego kompilację z narzędziami jak Incredibuild

1

Eiffel miał pojęcie o różnych stanach mrożone, i rekompilacji nie musi oznaczać, że cała klasa była rekompilacji.

Ile można zepsuć godne zaufania moduły i ile ich śledzisz?

2

W większości języków (całkiem dobrze wszystko poza C++), kompilacja pojedynczych jednostek kompilacji jest dość szybka.

Wiązanie/łączenie jest często powolne - linker musi odwoływać się do całego programu, a nie tylko do pojedynczej jednostki.

C++ cierpi jako - chyba że użyjesz idiomu pImpl - wymaga szczegółów implementacji każdego obiektu i wszystkich wbudowanych funkcji do kompilacji kodu klienta.

Java (źródło bytecode) cierpi, ponieważ gramatyka nie rozróżnia obiektów i klas - musisz załadować klasę Foo, aby sprawdzić, czy Foo.Bar.Baz jest polem Baz obiektu, do którego odnosi się pole statyczne Baru klasa Foo lub pole statyczne klasy Foo.Bar. Możesz dokonać zmiany w źródle klasy Foo między tymi dwoma i nie zmieniać źródła kodu klienta, ale nadal trzeba przekompilować kod klienta, ponieważ kod bajtowy rozróżnia dwa formularze, mimo że składnia nie . AFAIK Python bytecode nie rozróżnia dwóch modułów - są prawdziwymi członkami ich rodziców.

C++ i C cierpią, jeśli dodasz więcej nagłówków niż jest to wymagane, ponieważ preprocesor musi przetworzyć każdy nagłówek wiele razy, a kompilator je skompiluje. Zminimalizowanie rozmiaru i złożoności nagłówka pomaga, co sugeruje, że lepsza modułowość poprawi czas kompilacji.Nie zawsze jest możliwe kompilowanie nagłówków w pamięci podręcznej, ponieważ definicje, które występują podczas wstępnego przetwarzania nagłówka, mogą zmienić jego semantykę, a nawet składnię.

C cierpi, jeśli często korzystasz z preprocesora, ale rzeczywista kompilacja jest szybka; wiele kodu C używa typedef struct _X* X_ptr, aby ukryć implementację lepiej niż C++ - nagłówek C może łatwo składać się z typedefs i deklaracji funkcji, co zapewnia lepszą enkapsulację.

Proponuję, aby twój język ukrył szczegóły implementacji z kodu klienta, a jeśli jesteś językiem OO z zarówno członkami instancji, jak i przestrzeniami nazw, utwórz składnię, aby uzyskać dostęp do tych dwóch jednoznacznych. Zezwalaj na prawdziwe moduły, więc kod klienta musi tylko znać interfejs, a nie szczegóły implementacji. Nie zezwalaj na makr preprocesora lub inny mechanizm zmiany, aby zmienić semantykę przywoływanych modułów.

0

Prosty: upewnij się, że kompilator może natywnie korzystać z wielordzeniowych procesorów.

10

Twoim głównym problemem dzisiaj jest I/O. Twój procesor jest wielokrotnie szybszy niż pamięć główna, a pamięć jest około 1000 razy szybsza niż dostęp do dysku twardego.

Więc jeśli nie dokonasz obszernej optymalizacji kodu źródłowego, procesor spędza większość czasu, czekając na odczyt lub zapis danych.

Spróbuj tych zasad:

  1. Zaprojektuj swój kompilator pracować w kilku niezależnych etapów. Celem jest umożliwienie uruchomienia każdego kroku w innym wątku, aby można było używać wielordzeniowych procesorów. Pomoże to również zrównoleglić cały proces kompilacji (tj. Skompilować więcej niż jeden plik w tym samym czasie).

    Pozwoli to również załadować wiele plików źródłowych z wyprzedzeniem i przetworzyć je, aby właściwy etap kompilacji działał szybciej.

  2. Spróbuj pozwolić na kompilację plików niezależnie. Na przykład utwórz "brakującą pulę symboli" dla projektu. Brakujące symbole nie powinny powodować błędów kompilacji jako takich. Jeśli znajdziesz gdzieś brakujący symbol, usuń go z puli. Po skompilowaniu wszystkich plików sprawdź, czy pula jest pusta.

  3. Utwórz pamięć podręczną z ważnymi informacjami. Na przykład: Plik X używa symboli z pliku Y. W ten sposób można pominąć kompilowanie pliku Z (który nie odwołuje się do niczego w Y) po zmianie Y. Jeśli chcesz pójść o krok dalej, umieść wszystkie symbole zdefiniowane w dowolnym miejscu w puli. Jeśli plik zmienia się w taki sposób, że symbole są dodawane/usuwane, natychmiast zorientujesz się, które pliki są dotknięte (bez nawet ich otwarcia).

  4. Skompiluj w tle. Rozpocznij proces kompilatora, który sprawdza katalog projektu pod kątem zmian i kompiluje je, gdy tylko użytkownik zapisze plik. W ten sposób będziesz musiał skompilować tylko kilka plików za każdym razem, zamiast wszystkiego. Na dłuższą metę będziesz kompilował znacznie więcej, ale dla użytkownika czasy obrotu będą znacznie krótsze (= czas użytkownik musi czekać, aż będzie mogła uruchomić skompilowany wynik po zmianie).

  5. Użyj kompilatora "na czas" (tzn. Skompiluj plik, gdy jest używany, na przykład w poleceniu import). Projekty są następnie dystrybuowane w formie źródłowej i kompilowane po uruchomieniu po raz pierwszy. Python to robi. Aby to wykonać, możesz wstępnie skompilować bibliotekę podczas instalacji kompilatora.

  6. Nie używaj plików nagłówkowych. Przechowuj wszystkie informacje w jednym miejscu i generuj pliki nagłówkowe ze źródła, jeśli musisz.Może przechowywać pliki nagłówkowe tylko w pamięci i nigdy nie zapisuj ich na dysku.

+0

To dobrze, ale pominąłeś najważniejszą rzecz: plik we/wy. –

+0

@ david.pfx: file I/O jest danym problemem. Przestrzeganie reguł stworzy proces, który będzie najmniej zależny od operacji we/wy. –

+0

Jak już powiedziałem, odpowiedź zawiera wiele dobrych pomysłów, ale jeśli nie koncentrujesz się wyraźnie na szukaniu sposobów na przyspieszenie operacji we/wy, wydajność nie będzie następować. Inaczej mówiąc, jeśli twój kompilator zakończy operację We/Wy, żaden z mądrych wątków ani buforowania nie zrobi żadnej różnicy. Myślę, że twoja odpowiedź poprawiłaby się, biorąc pod uwagę tę kwestię. –

1
  • Bądź gramatyka proste i jednoznaczne, a zatem szybkie i łatwe do analizowania.
  • Należy wprowadzić silne ograniczenia dotyczące włączania plików.
  • Zezwalaj na kompilację bez pełnej informacji w miarę możliwości (np. Predeclaration w C i C++).
  • Jednoprzebiegowa kompilacja, jeśli to możliwe.
0
  • Upewnij się, że wszystko może zostać skompilowane podczas pierwszej kompilacji. Na przykład. zakazuj przekazywania odwołań.
  • Skorzystaj z gramatyki bezkontekstowej, aby znaleźć poprawne drzewo analizy bez tabeli symboli.
  • Upewnij się, że semantykę można wywnioskować ze składni, dzięki czemu możesz skonstruować poprawną AST bezpośrednio, a nie przez zgniatanie z drzewem analizy i tabelą symboli.
0

Jak poważny jest to kompilator?

O ile składnia nie jest dość skomplikowana, parser powinien być w stanie uruchomić nie więcej niż 10-100 razy wolniej niż po indeksowaniu znaków pliku wejściowego.

Podobnie, generowanie kodu powinno być ograniczone przez formatowanie wyjściowe.

Nie powinieneś mieć problemów z wydajnością, chyba że robisz duży, poważny kompilator, potrafiący obsługiwać aplikacje wielominiowe z dużą ilością plików nagłówkowych.

Następnie trzeba się martwić o wstępnie skompilowane nagłówki, przejścia optymalizacji i łączenie.

3

Sam zaimplementowałem kompilator, a skończyło się na tym, że gdy ludzie zaczęli dodawać setki plików źródłowych. Byłem bardzo zaskoczony tym, co odkryłem.

Okazuje się, że najważniejszą rzeczą, którą można zoptymalizować, nie jest gramatyka. To nie jest twój analizator leksykalny ani twój analizator składni. Zamiast tego, najważniejszą rzeczą pod względem szybkości jest kod, który odczytuje pliki źródłowe z dysku. We/wy na dysku są powolne. Bardzo wolno. Możesz w przybliżeniu zmierzyć szybkość kompilatora przez liczbę wykonanych operacji we/wy dysku.

Okazuje się, że najlepszą rzeczą, jaką można zrobić, aby przyspieszyć kompilator, jest wczytanie całego pliku do pamięci w jednym dużym I/O, wykonanie wszystkich leksykowania, parsowania itp. Z pamięci RAM, a następnie wypisz wynik na dysku w jednym dużym I/O.

Rozmawiałem z jednym z głównych facetów prowadzących Gnat (kompilator GCC Ada) o tym, a on powiedział mi, że on faktycznie użył umieścić wszystko, co mógł na dyskach RAM, tak że nawet jego plik I/O był naprawdę tylko RAM czyta i pisze.

+0

Myślałem o użyciu nazwanego potoku zamiast pliku i innych sposobów, aby zachować go w pamięci RAM. Nie mogłem wymyślić sposobu na ulepszenie łączenia. Czy mogę Ci wysłać e-mail do mnie myusername @ gmail.com mam mnóstwo pytań dla Ciebie. –

+0

Nie będę przechodził herkulesowych wysiłków, zanim będziesz miał szansę zmierzyć rzeczy. Przedwczesna optymalizacja i cokolwiek. Po prostu upewnij się, że front-end do leksykonu jest na tyle modułowy, że zastąpi go później API bufora. –

+1

Absolutnie poprawne. W przypadku większości języków czas wejścia/wyjścia pliku (I/O time) absolutnie dominuje nad wszystkim innym. Lexer też wnosi swój wkład. Wszystko inne jest odległym trzecim. –

1

Jednej rzeczy zaskakująco brakuje w odpowiedziach do tej pory: sprawiają, że robisz gramatykę bez kontekstu, itp. Dobrze spójrz na języki zaprojektowane przez Wirtha, takie jak Pascal & Modula-2. Nie musisz ponownie wdrażać Pascala, ale gramatyka została zaprojektowana specjalnie do szybkiego kompilowania. Następnie sprawdź, czy możesz znaleźć jakieś stare artykuły na temat sztuczek, które Anders wprowadził do implementacji Turbo Pascala. Wskazówka: sterowany tabelą.

0

Nie widziałem zbyt wiele pracy, aby zminimalizować czas kompilacji. Ale niektóre pomysły przychodzą mi na myśl:

  1. Zachowaj prostotę gramatyki. Pogorszona gramatyka zwiększy czas kompilacji.
  2. Spróbuj użyć paralelizmu, używając wielordzeniowego procesora graficznego lub procesora.
  3. Porównywanie współczesnego kompilatora i zobacz, jakie są wąskie gardła i co możesz zrobić w swoim kompilatorze/języku, aby ich uniknąć.

ile piszesz wysoce specjalistycznego języka kompilacji nie jest naprawdę problem ..

0

Dokonaj kompilacji systemu, który nie ssać!

Istnieje ogromna liczba programów, które mogą zawierać 3 pliki źródłowe, których skompilowanie zajmuje mniej niż sekundę, ale zanim dotrzesz tak daleko, musisz przeskanować skrypt automake, który zajmuje około 2 minut, sprawdzając takie rzeczy jak rozmiar int. A jeśli przejdziesz do kompilacji czegoś jeszcze minutę później, to sprawisz, że będziesz siedział prawie dokładnie w tym samym zestawie testów.

Więc chyba kompilator jest robi straszne rzeczy dla użytkownika jak zmiana rozmiaru jego int s lub zmieniając podstawowe implementacje funkcji pomiędzy przebiegami, po prostu zrzucić te informacje do pliku i niech się go w drugim zamiast 2 minuty.

2

Oto kilka sztuczek wydajności, które nauczyliśmy się poprzez pomiar prędkości kompilacji i co wpływa na to:

  • Napisz dwa przejścia kompilatora: znaki IR, IR do kodu. (Łatwiej napisać trzy -pass kompilator, że idzie znaków -> AST -> IR -> kod, ale to nie jest tak szybki.)

  • Jako następstwo, nie mają optymalizatora; ciężko jest napisać szybki optymalizator.

  • Rozważ generowanie kodu bajtowego zamiast natywnego kodu maszynowego. Maszyna wirtualna dla Lua jest dobrym modelem.

  • Spróbuj alokatora rejestru liniowego lub prostego przydziału rejestru, który Fraser i Hanson używali w lcc.

  • W prostym kompilatorze analiza leksykalna jest często największym wąskim gardłem wydajności. Jeśli piszesz kod C lub C++, użyj re2c. Jeśli używasz innego języka (który okaże się o wiele przyjemniejszy), przeczytaj artykuł dotyczący re2c i zastosuj wyciągnięte wnioski.

  • Generowanie kodu przy użyciu maksymalnego chrupania lub ewentualnie iburg.

  • Co zaskakujące, asembler GNU jest wąskim gardłem w wielu kompilatorach. Jeśli możesz bezpośrednio wygenerować plik binarny, zrób to. Lub sprawdź New Jersey Machine-Code Toolkit.

  • Jak wspomniano powyżej, zaprojektuj swój język, aby uniknąć czegoś takiego jak #include. Albo nie używaj plików interfejsu, albo prekompiluj pliki interfejsu.Ta taktyka radykalnie redukuje burdern na lexer, który, jak powiedziałem, jest często największym wąskim gardłem.

+0

IR oznacza co? szukam teraz tych informacji, czy analiza leksykalna nie stanowi już problemu? Myślałem, że IO jest głównym problemem (jak w twojej analizie, czytałem wiele plików, a nie rzeczywistą złożoność). Mój język planuję być złożony. –

+0

IR oznacza reprezentację pośrednią. W prostym kompilatorze lexing jest nadal drogi; pętla dotyka każdego znaku wejścia. Chcesz * obie *, aby utrzymać całkowity rozmiar wejściowy * i *, aby leksykon był efektywny. Złożona gramatyka jest w porządku. –

Powiązane problemy