Kompilacja pliku C ++ zajmuje bardzo dużo czasu w porównaniu do C # i Java. Skompilowanie pliku C ++ zajmuje znacznie więcej czasu niż uruchomienie skryptu Python o normalnym rozmiarze. Obecnie używam VC ++, ale jest tak samo z każdym kompilatorem. Dlaczego to?
Dwa powody, dla których mogłem wymyślić, to ładowanie plików nagłówkowych i uruchamianie preprocesora, ale nie wydaje się, żeby to wyjaśniało, dlaczego zajmuje to tak długo.
c++
performance
compilation
Dan Goldstein
źródło
źródło
It takes significantly longer to compile a C++ file
- masz na myśli 2 sekundy w porównaniu do 1 sekundy? Z pewnością jest to dwa razy więcej, ale mało znaczące. A może masz na myśli 10 minut w porównaniu do 5 sekund? Proszę określić ilościowo.Odpowiedzi:
Kilka powodów
Pliki nagłówkowe
Każda jednostka kompilacji wymaga (1) załadowania i (2) skompilowania setek, a nawet tysięcy nagłówków. Każdy z nich zazwyczaj musi zostać ponownie skompilowany dla każdej jednostki kompilacji, ponieważ preprocesor zapewnia, że wynik kompilacji nagłówka może się różnić między każdą jednostką kompilacji. (Makro może być zdefiniowane w jednej jednostce kompilacji, która zmienia zawartość nagłówka).
Jest to prawdopodobnie Głównym powodem, ponieważ wymaga ogromnych ilości kodu zostać skompilowane dla każdej jednostki kompilacji, a dodatkowo, każdy nagłówek musi być skompilowany wiele razy (raz dla każdej jednostki kompilacji, która zawiera go).
Łączenie
Po skompilowaniu wszystkie pliki obiektowe muszą zostać połączone. Zasadniczo jest to monolityczny proces, którego nie da się zrównoważyć, i musi on przetworzyć cały projekt.
Rozbiór gramatyczny zdania
Składnia jest wyjątkowo skomplikowana do przeanalizowania, zależy w dużym stopniu od kontekstu i bardzo trudno jest ją jednoznacznie określić. To zajmuje dużo czasu.
Szablony
W języku C #
List<T>
jest to jedyny typ, który jest kompilowany, bez względu na to, ile wystąpień listy masz w swoim programie. W C ++vector<int>
jest całkowicie odrębnym typemvector<float>
i każdy będzie musiał zostać skompilowany osobno.Dodaj do tego, że szablony tworzą pełny „podjęzyk” Turinga, który kompilator musi interpretować, a to może stać się absurdalnie skomplikowane. Nawet stosunkowo prosty kod metaprogramowania szablonów może definiować szablony rekurencyjne, które tworzą dziesiątki instancji szablonów. Szablony mogą również skutkować wyjątkowo złożonymi typami, z absurdalnie długimi nazwami, co dodaje dodatkowej pracy do linkera. (Musi porównać wiele nazw symboli, a jeśli nazwy te mogą urosnąć do wielu tysięcy znaków, mogą stać się dość drogie).
I oczywiście zaostrzają problemy z plikami nagłówkowymi, ponieważ szablony zwykle muszą być definiowane w nagłówkach, co oznacza, że o wiele więcej kodu musi zostać przeanalizowane i skompilowane dla każdej jednostki kompilacji. W zwykłym kodzie C nagłówek zazwyczaj zawiera tylko deklaracje przesyłania, ale bardzo mało rzeczywistego kodu. W C ++ nierzadko prawie cały kod znajduje się w plikach nagłówkowych.
Optymalizacja
C ++ pozwala na bardzo dramatyczne optymalizacje. C # lub Java nie pozwalają na całkowite wyeliminowanie klas (muszą one tam być w celu odbicia), ale nawet prosty metaprogram szablonu C ++ może z łatwością wygenerować dziesiątki lub setki klas, z których wszystkie są uwzględniane i ponownie eliminowane podczas optymalizacji faza.
Ponadto kompilator musi w pełni zoptymalizować program C ++. Program AC # może polegać na kompilatorze JIT w celu wykonywania dodatkowych optymalizacji w czasie ładowania, C ++ nie ma takich „drugich szans”. To, co generuje kompilator, jest tak zoptymalizowane, jak to tylko możliwe.
Maszyna
C ++ jest kompilowany do kodu maszynowego, który może być nieco bardziej skomplikowany niż użycie kodu bajtowego Java lub .NET (szczególnie w przypadku x86). (Jest to wspomniane kompletnie tylko dlatego, że zostało wspomniane w komentarzach itp. W praktyce jest mało prawdopodobne, aby krok ten zajął więcej niż niewielką część całkowitego czasu kompilacji).
Wniosek
Większość tych czynników jest współdzielona przez kod C, który faktycznie kompiluje się dość skutecznie. Etap analizy jest w C ++ o wiele bardziej skomplikowany i może zająć znacznie więcej czasu, ale głównym przestępcą są prawdopodobnie szablony. Są użyteczne i sprawiają, że C ++ jest znacznie potężniejszym językiem, ale także odbijają się na szybkości kompilacji.
źródło
Spowolnienie niekoniecznie jest takie samo w przypadku jakiegokolwiek kompilatora.
Nie używałem Delphi ani Kylix, ale w czasach MS-DOS program Turbo Pascal skompilowałby się niemal natychmiast, podczas gdy równoważny program Turbo C ++ po prostu zaindeksowałby.
Dwie główne różnice to bardzo mocny system modułowy i składnia umożliwiająca kompilację jednoprzebiegową.
Jest oczywiście możliwe, że szybkość kompilacji po prostu nie była priorytetem dla programistów kompilatorów C ++, ale istnieją pewne nieodłączne komplikacje w składni C / C ++, które utrudniają przetwarzanie. (Nie jestem ekspertem od C, ale Walter Bright jest i po zbudowaniu różnych komercyjnych kompilatorów C / C ++ stworzył język D. Jedną z jego zmian było wymuszenie gramatyki bezkontekstowej, aby ułatwić analizowanie języka .)
Zauważysz również, że ogólnie Pliki Makefile są skonfigurowane tak, że każdy plik jest kompilowany osobno w C, więc jeśli 10 plików źródłowych używa tego samego pliku dołączania, ten plik dołączania jest przetwarzany 10 razy.
źródło
Parsowanie i generowanie kodu jest w rzeczywistości dość szybkie. Prawdziwym problemem jest otwieranie i zamykanie plików. Pamiętaj, że nawet przy włączonych strażnikach kompilator nadal ma otwarty plik .H i czyta każdą linię (a następnie ją ignoruje).
Kiedyś przyjaciel (znudzony pracą), wziął aplikację swojej firmy i umieścił wszystko - wszystkie pliki źródłowe i nagłówkowe - w jednym dużym pliku. Czas kompilacji spadł z 3 godzin do 7 minut.
źródło
Innym powodem jest użycie preprocesora C do lokalizowania deklaracji. Nawet w przypadku nagłówków nagłówek .h wciąż musi być analizowany w kółko, za każdym razem, gdy są uwzględniane. Niektóre kompilatory obsługują wstępnie skompilowane nagłówki, które mogą w tym pomóc, ale nie zawsze są używane.
Zobacz także: C ++ Często zadawane pytania
źródło
C ++ jest kompilowany do kodu maszynowego. Masz więc procesor wstępny, kompilator, optymalizator i wreszcie asembler, z których wszystkie muszą działać.
Java i C # są kompilowane w bajt-code / IL, a maszyna wirtualna Java / .NET Framework wykonuje się (lub JIT kompiluje w kod maszynowy) przed wykonaniem.
Python jest językiem interpretowanym, który jest również kompilowany do kodu bajtowego.
Jestem pewien, że istnieją również inne powody, ale ogólnie rzecz biorąc, brak konieczności kompilacji do natywnego języka maszynowego oszczędza czas.
źródło
Największe problemy to:
1) Nieskończone przetwarzanie nagłówka. Już wspomniane. Ograniczenia (jak raz #pragma) zwykle działają tylko na jednostkę kompilacji, a nie na kompilację.
2) Fakt, że zestaw narzędzi jest często podzielony na wiele plików binarnych (marka, preprocesor, kompilator, asembler, archiwizator, impdef, linker i dlltool w skrajnych przypadkach), które muszą ponownie zainicjować i ponownie załadować cały stan dla każdego wywołania ( kompilator, asembler) lub co kilka plików (archiwizator, linker i dlltool).
Zobacz także tę dyskusję na temat comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078, szczególnie ta:
http://compilers.iecc.com/comparch/article/02-07-128
Zauważ, że John, moderator kompilatorów kompilacji, wydaje się zgadzać, a to oznacza, że powinno być możliwe osiągnięcie podobnych prędkości również dla C, jeśli w pełni zintegrujesz łańcuch narzędzi i zaimplementujesz prekompilowane nagłówki. Wiele komercyjnych kompilatorów C robi to do pewnego stopnia.
Zauważ, że uniksowy model faktoryzacji wszystkiego do osobnego pliku binarnego jest rodzajem najgorszego modelu dla Windows (z powolnym tworzeniem procesu). Jest to bardzo zauważalne przy porównywaniu czasów kompilacji GCC między Windows a * nix, szczególnie jeśli system make / config wywołuje również niektóre programy tylko w celu uzyskania informacji.
źródło
Budowanie C / C ++: co tak naprawdę się dzieje i dlaczego zajmuje tak dużo czasu
Stosunkowo duża część czasu na tworzenie oprogramowania nie jest poświęcana na pisanie, uruchamianie, debugowanie, a nawet projektowanie kodu, ale na oczekiwanie na zakończenie kompilacji. Aby wszystko działało szybko, musimy najpierw zrozumieć, co się dzieje, gdy kompilowane jest oprogramowanie C / C ++. Kroki są z grubsza następujące:
Teraz przyjrzymy się każdemu krokowi bardziej szczegółowo, koncentrując się na tym, jak można je przyspieszyć.
Konfiguracja
To pierwszy krok od rozpoczęcia budowy. Zwykle oznacza uruchomienie skryptu konfiguracyjnego lub CMake, Gyp, SCons lub innego narzędzia. Może to zająć od jednej sekundy do kilku minut w przypadku bardzo dużych skryptów konfiguracyjnych opartych na Autotools.
Ten krok zdarza się stosunkowo rzadko. Należy go uruchomić tylko podczas zmiany konfiguracji lub konfiguracji kompilacji. Poza zmieniającymi się systemami kompilacji niewiele można zrobić, aby ten krok był szybszy.
Zbuduj uruchomienie narzędzia
Tak się dzieje po uruchomieniu make lub kliknięciu ikony kompilacji w IDE (która jest zwykle aliasem make). Plik binarny narzędzia do budowania uruchamia się i odczytuje pliki konfiguracyjne oraz konfigurację kompilacji, które zwykle są tym samym.
W zależności od złożoności i wielkości kompilacji może to zająć od ułamka sekundy do kilku sekund. Samo w sobie nie byłoby tak źle. Niestety większość systemów budowania opartych na marce powoduje, że make jest wywoływany dziesiątki do setek razy dla każdego kompilacji. Zwykle jest to spowodowane rekurencyjnym użyciem make (co jest złe).
Należy zauważyć, że powodem, dla którego Make jest tak wolny, nie jest błąd implementacyjny. Składnia Makefiles ma pewne dziwactwa, które sprawiają, że naprawdę szybka implementacja jest prawie niemożliwa. Problem ten jest jeszcze bardziej zauważalny w połączeniu z następnym krokiem.
Sprawdzanie zależności
Po przeczytaniu konfiguracji narzędzie do budowania musi określić, które pliki uległy zmianie i które należy ponownie skompilować. Pliki konfiguracyjne zawierają ukierunkowany wykres acykliczny opisujący zależności kompilacji. Ten wykres jest zwykle budowany podczas etapu konfiguracji. Czas uruchamiania narzędzia kompilacji i skaner zależności są uruchamiane na każdej kompilacji. Ich łączny czas pracy określa dolną granicę cyklu edycji-kompilacji-debugowania. W przypadku małych projektów czas ten wynosi zwykle kilka sekund. To jest tolerowane. Istnieją alternatywy dla Make. Najszybszym z nich jest Ninja, która została zbudowana przez inżynierów Google dla Chromium. Jeśli używasz CMake lub Gyp do budowania, po prostu przełącz się na ich zaplecza Ninja. Nie musisz nic zmieniać w samych plikach kompilacji, po prostu ciesz się przyspieszeniem. Ninja nie jest jednak pakowana w większości dystrybucji,
Kompilacja
W tym momencie w końcu wywołujemy kompilator. Cięcie niektórych rogów, oto przybliżone kroki, które zostały wykonane.
Wbrew powszechnemu przekonaniu, kompilacja C ++ nie jest wcale taka wolna. STL działa wolno, a większość narzędzi do kompilacji używanych do kompilacji C ++ działa wolno. Istnieją jednak szybsze narzędzia i sposoby łagodzenia wolnych części języka.
Ich użycie wymaga trochę smaru łokciowego, ale korzyści są niezaprzeczalne. Szybsze czasy kompilacji prowadzą do szczęśliwszych programistów, większej zwinności i ostatecznie lepszego kodu.
źródło
Skompilowany język zawsze będzie wymagał większego obciążenia początkowego niż język interpretowany. Ponadto być może nie ułożyłeś dobrze kodu C ++. Na przykład:
Kompiluje się dużo wolniej niż:
źródło
Prostym sposobem na skrócenie czasu kompilacji w większych projektach C ++ jest utworzenie pliku * .cpp zawierającego wszystkie pliki cpp w projekcie i skompilowanie go. Zmniejsza to problem wybuchu nagłówka do jednego razu. Zaletą tego jest to, że błędy kompilacji nadal będą odnosić się do poprawnego pliku.
Załóżmy na przykład, że masz pliki a.cpp, b.cpp i c.cpp .. utwórz plik: everything.cpp:
Następnie skompiluj projekt, tworząc po prostu everything.cpp
źródło
Niektóre powody to:
1) Gramatyka C ++ jest bardziej złożona niż C # lub Java i zajmuje więcej czasu na analizę.
2) (Co ważniejsze) Kompilator C ++ tworzy kod maszynowy i wykonuje wszystkie optymalizacje podczas kompilacji. C # i Java idą tylko do połowy i pozostaw te kroki JIT.
źródło
Kompromis, który otrzymujesz, polega na tym, że program działa trochę szybciej. Może to być dla ciebie zimnym komfortem podczas programowania, ale może mieć duże znaczenie po zakończeniu programowania, a program jest po prostu uruchamiany przez użytkowników.
źródło
Większość odpowiedzi jest nieco niejasna, gdy wspomina się, że C # zawsze będzie działał wolniej ze względu na koszt wykonywania działań, które w C ++ są wykonywane tylko raz w czasie kompilacji, ten koszt wydajności ma również wpływ ze względu na zależności środowiska wykonawczego (więcej rzeczy do załadowania, aby móc do uruchomienia), nie wspominając o tym, że programy w języku C # zawsze będą miały więcej pamięci, co skutkuje ściślejszą wydajnością związaną z możliwościami dostępnego sprzętu. To samo dotyczy innych języków, które są interpretowane lub zależą od maszyny wirtualnej.
źródło
Są dwa problemy, które mogą mieć wpływ na szybkość kompilacji twoich programów w C ++.
MOŻLIWE ZAGADNIENIE nr 1 - KOMPILACJA NAGŁÓWKA: (To może być lub nie być rozwiązane przez inną odpowiedź lub komentarz.) Microsoft Visual C ++ (AKA VC ++) obsługuje wstępnie skompilowane nagłówki, które bardzo polecam. Po utworzeniu nowego projektu i wybraniu rodzaju tworzonego programu na ekranie powinno pojawić się okno kreatora instalacji. Jeśli naciśniesz przycisk „Dalej>” u dołu, okno przeniesie Cię do strony, która ma kilka list funkcji; upewnij się, że pole obok opcji „Prekompilowany nagłówek” jest zaznaczone. (UWAGA: Takie było moje doświadczenie z aplikacjami konsolowymi Win32 w C ++, ale może nie być tak w przypadku wszystkich rodzajów programów w C ++.)
MOŻLIWA KWESTIA # 2 - LOKALIZACJA, DO KTÓREJ JEST KOMPILACJA: Tego lata wziąłem kurs programowania i musieliśmy przechowywać wszystkie nasze projekty na dyskach flash 8 GB, ponieważ komputery w laboratorium, z którego korzystaliśmy, były czyszczone co noc o północy, co wymazałoby całą naszą pracę. Jeśli kompilujesz się na zewnętrzne urządzenie pamięci masowej ze względu na przenośność / bezpieczeństwo / itp., Może to potrwać bardzo długoczas (nawet z wcześniej skompilowanymi nagłówkami, o których wspomniałem powyżej) na kompilację programu, szczególnie jeśli jest to dość duży program. Radzę ci w tym przypadku tworzyć i kompilować programy na dysku twardym komputera, z którego korzystasz, a gdy chcesz / musisz przerwać pracę nad projektem (projektami) z jakiegokolwiek powodu, przenieś je do zewnętrznego urządzenie pamięci, a następnie kliknij ikonę „Bezpiecznie usuń sprzęt i wysuń nośnik”, która powinna pojawić się jako mały dysk flash za małym zielonym kółkiem z białym znacznikiem wyboru, aby go odłączyć.
Mam nadzieję, że to Ci pomoże; daj mi znać, jeśli tak! :)
źródło