Obecnie używam GCC, ale niedawno odkryłem Clanga i zastanawiam się nad zmianą. Jest jednak jeden decydujący czynnik - jakość (szybkość, wielkość pamięci, niezawodność) tworzonych plików binarnych - jeśli gcc -O3
można wytworzyć plik binarny, który działa 1% szybciej lub zajmuje 1% mniej pamięci, jest to przełom.
Clang może pochwalić się lepszymi prędkościami kompilacji i mniejszą pamięcią czasu kompilacji niż GCC, ale naprawdę interesują mnie testy porównawcze / porównania powstałego skompilowanego oprogramowania - czy możesz wskazać mi jakieś lub opisać swoje doświadczenia?
Odpowiedzi:
Oto kilka aktualnych, choć wąskich ustaleń z GCC 4.7.2 i Clang 3.2 dla C ++.
AKTUALIZACJA: Załączono poniżej porównanie GCC 4.8.1 v clang 3.3.
AKTUALIZACJA: Dołączono do tego porównanie GCC 4.8.2 v clang 3.4.
Utrzymuję narzędzie OSS, które jest zbudowane dla Linuksa z GCC i Clang oraz z kompilatorem Microsoft dla Windows. Narzędzie, coan, jest preprocesorem i analizatorem plików źródłowych C / C ++ i takich linii kodowych: jego profil obliczeniowy specjalizuje się w parsowaniu rekurencyjnym i obsłudze plików. Dział rozwoju (do którego odnoszą się te wyniki) obejmuje obecnie około 11 KB LOC w około 90 plikach. Jest teraz kodowany w C ++, który jest bogaty w polimorfizm i szablony, ale wciąż jest pogrążony w wielu łatach przez swoją nie tak daleką przeszłość w zhakowanych razem C. Semantyka ruchu nie jest wyraźnie wykorzystywana. Jest jednowątkowy. Nie poświęciłem żadnego poważnego wysiłku na jego optymalizację, podczas gdy „architektura” pozostaje w dużej mierze do zrobienia.
Użyłem Clanga przed 3.2 tylko jako eksperymentalny kompilator, ponieważ pomimo doskonałej prędkości kompilacji i diagnostyki, jego obsługa standardu C ++ 11 pozostawała w tyle za współczesną wersją GCC pod względem wykonywanym przez Coana. W wersji 3.2 luka ta została zamknięta.
Moja wiązka testowa systemu Linux do bieżących procesów programistycznych Coan z grubsza 70 000 plików źródłowych w mieszaninie przypadków analizatora składni z jednym plikiem, testy warunków skrajnych zużywa tysiące plików i testy scenariuszy z plikami mniejszymi niż 1 KB. Oprócz raportowania wyników testu, uprząż gromadzi i wyświetla sumy zużytych plików i czasu pracy zużytego w Coan (po prostu przekazuje każdą linię poleceń Coan do
time
polecenia Linux i przechwytuje i sumuje zgłoszone liczby). Czasy są pochlebne przez fakt, że dowolna liczba testów, które wymagają 0 mierzalnego czasu, zsumuje się do 0, ale wkład takich testów jest znikomy. Statystyki czasu są wyświetlane na końcu wmake check
następujący sposób:Porównałem wydajność wiązki testowej między GCC 4.7.2 a Clang 3.2, przy czym wszystkie rzeczy były takie same, z wyjątkiem kompilatorów. Począwszy od wersji Clang 3.2, nie wymagam już żadnego preprocesora rozróżnienia między traktami kodu, które GCC będzie kompilować, a alternatywami Clanga. W każdym przypadku zbudowałem tę samą bibliotekę C ++ (GCC) i przeprowadziłem wszystkie porównania kolejno w tej samej sesji terminalowej.
Domyślny poziom optymalizacji dla mojej wersji wydania to -O2. Z powodzeniem testowałem również kompilacje w -O3. Testowałem każdą konfigurację 3 razy jeden po drugim i uśredniłem 3 wyniki, z następującymi wynikami. Liczba w komórce danych to średnia liczba mikrosekund zużywanych przez plik wykonywalny Coana do przetworzenia każdego z plików wejściowych ~ 70K (odczyt, parsowanie i zapisywanie danych wyjściowych oraz diagnostyka).
Każda konkretna aplikacja najprawdopodobniej ma cechy, które działają niesprawiedliwie w stosunku do mocnych i słabych stron kompilatora. Rygorystyczne testy porównawcze wykorzystują różnorodne aplikacje. Mając to na uwadze, godnymi uwagi cechami tych danych są:
Kolejne interesujące porównanie dwóch kompilatorów pojawiło się przypadkiem wkrótce po tych ustaleniach. Coan swobodnie stosuje inteligentne wskaźniki, a jeden z nich jest mocno wyćwiczony w obsłudze plików. Ten konkretny typ inteligentnego wskaźnika został wpisany we wcześniejszych wersjach ze względu na różnicowanie kompilatora, aby był
std::unique_ptr<X>
skonfigurowany, jeśli skonfigurowany kompilator miał wystarczająco dojrzałe wsparcie dla jego użycia jako takiego, a poza tymstd::shared_ptr<X>
. Odchyleniestd::unique_ptr
było głupie, ponieważ wskaźniki te faktycznie zostały przeniesione, alestd::unique_ptr
wyglądały jak opcja montera do zastąpieniastd::auto_ptr
w momencie, gdy warianty C ++ 11 były dla mnie nowością.W trakcie eksperymentalnych kompilacji w celu oceny ciągłej potrzeby tego i podobnego różnicowania w Clang 3.2, nieumyślnie zbudowałem,
std::shared_ptr<X>
gdy miałem zamiar budowaćstd::unique_ptr<X>
, i byłem zaskoczony, że wynikowy plik wykonywalny, z domyślną optymalizacją -O2, był najszybszy I widziałem, czasem osiągając 184 msek. na plik wejściowy. Przy tej jednej zmianie w kodzie źródłowym były to odpowiednie wyniki;Ważnymi punktami tutaj są:
Przed i po zmianie typu inteligentnego wskaźnika Clang jest w stanie zbudować znacznie szybszy plik wykonywalny Coana przy optymalizacji -O3 i może zbudować równie szybszy plik wykonywalny przy -O2 i -O3, gdy ten typ wskaźnika jest najlepszy -
std::shared_ptr<X>
- dla pracy.Oczywistym pytaniem, którego nie jestem kompetentny do skomentowania, jest to, dlaczego Clang powinien być w stanie znaleźć przyspieszenie o 25% O2 w mojej aplikacji, gdy często używany typ inteligentnego wskaźnika zmienia się z unikalnego na współdzielony, podczas gdy GCC jest obojętny do tej samej zmiany. Nie wiem też, czy powinienem kibicować, czy wygłupiać się odkryciem, że optymalizacja Clang -O2 kryje w sobie tak ogromną wrażliwość na mądrość moich mądrych wskazówek.
AKTUALIZACJA: GCC 4.8.1 v clang 3.3
Odpowiednie wyniki są teraz:
Fakt, że wszystkie cztery pliki wykonywalne zajmują teraz znacznie więcej czasu niż poprzednio, aby przetworzyć 1 plik, nie ma wpływu na wydajność najnowszych kompilatorów. Wynika to z faktu, że późniejsza gałąź programistyczna aplikacji testowej nabrała w międzyczasie wielu parsowania i szybko za to płaci. Istotne są tylko stosunki.
Najważniejsze kwestie nie są teraz zaskakująco nowe:
Porównując te wyniki z wynikami dla GCC 4.7.2 i clang 3.2, wyróżnia się, że GCC cofnęło około jednej czwartej przewagi clanga na każdym poziomie optymalizacji. Ponieważ jednak aplikacja testowa została w międzyczasie bardzo rozbudowana, nie można tego z pewnością przypisać do nadrabiania zaległości w generowaniu kodu GCC. (Tym razem zauważyłem migawkę aplikacji, z której uzyskano taktowanie i mogę z niej ponownie korzystać.)
AKTUALIZACJA: GCC 4.8.2 v clang 3.4
Ukończyłem aktualizację dla GCC 4.8.1 v Clang 3.3, mówiąc, że będę trzymał się tej samej migawki Coan w celu dalszych aktualizacji. Ale zamiast tego zdecydowałem się przetestować tę migawkę (wersja 301) i najnowszą migawkę programistyczną, która przeszła pomyślnie przez zestaw testów (wersja 619). To daje trochę długości geograficznej, a miałem inny motyw:
W moim pierwotnym poście zauważyłem, że nie poświęciłem żadnego wysiłku na optymalizację coana pod kątem szybkości. Tak było nadal od wersji rev. 301. Jednak po tym, jak wbudowałem przyrząd do pomiaru czasu w uprząż testową Coana, za każdym razem, gdy uruchamiałem zestaw testowy, wpatrywał się we mnie wpływ najnowszych zmian na wydajność. Widziałem, że często był on zaskakująco duży i że trend był bardziej negatywny, niż czułem, że zasługują na mnie korzyści z funkcjonalności.
Przez rev. 308 średni czas przetwarzania na plik wejściowy w pakiecie testowym znacznie wzrósł ponad dwukrotnie od czasu pierwszego opublikowania tutaj. W tym momencie dokonałem zwrotu w mojej 10-letniej polityce nie zawracania sobie głowy wydajnością. W intensywnym szeregu zmian zawsze brano pod uwagę wydajność do 619, a duża liczba z nich poszła wyłącznie na przepisanie kluczowych nośników na zasadniczo szybszych liniach (choć bez użycia do tego żadnych niestandardowych funkcji kompilatora). Byłoby ciekawie zobaczyć reakcję każdego kompilatora na ten zwrot,
Oto znana już macierz czasowa dla najnowszych dwóch kompilatorów kompilacji rev.301:
coan - rev.301 wyniki
Historia tutaj jest tylko nieznacznie zmieniona z GCC-4.8.1 i Clang-3.3. Pokazywanie GCC jest trochę lepsze. Clang's jest trochę gorszy. Hałas mógłby to wyjaśnić. Clang wciąż wychodzi przed siebie
-O2
i-O3
marże, które nie miałyby znaczenia w większości aplikacji, ale miałyby znaczenie dla kilku.A oto macierz dla rev. 619.
coan - rev.619 wyników
Biorąc obok siebie cyfry 301 i 619, wypowiada się kilka punktów.
Chciałem napisać szybszy kod, a oba kompilatory zdecydowanie potwierdzają moje wysiłki. Ale:
GCC spłaca te wysiłki o wiele hojniej niż Clang. Przy
-O2
optymalizacji Clang 619 kompilacji jest 46% szybszy niż 301 kompilacji: przy-O3
poprawie Clanga wynosi 31%. Dobrze, ale na każdym poziomie optymalizacji wersja 619 GCC jest ponad dwa razy szybsza niż 301.GCC bardziej niż odwraca dawną wyższość Clanga. Na każdym poziomie optymalizacji GCC bije teraz Clanga o 17%.
Zdolność Clanga w kompilacji 301 do uzyskania większej dźwigni niż GCC z
-O3
optymalizacji zniknęła w kompilacji 619. Żaden kompilator nie zyskuje znacząco-O3
.Byłem wystarczająco zaskoczony tym odwróceniem losu, że podejrzewałem, że mógłbym przypadkowo stworzyć powolną kompilację samego klangu 3.4 (ponieważ zbudowałem go ze źródła). Więc ponownie przeprowadziłem test 619 z zapasem mojej dystrybucji Clang 3.3. Wyniki były praktycznie takie same jak dla 3.4.
Jeśli chodzi o reakcję na zawrót: pod tym względem Clang radził sobie znacznie lepiej niż GCC z szybkością wyciskania z mojego kodu C ++, kiedy nie dawałem mu żadnej pomocy. Kiedy postanowiłem pomóc, GCC wykonało znacznie lepszą robotę niż Clang.
Nie podniosę tej obserwacji do zasady, ale czerpię naukę, że „Który kompilator produkuje lepsze pliki binarne?” jest pytaniem, które nawet jeśli określisz zestaw testów, dla którego odpowiedź powinna być względna, nadal nie jest jednoznaczną kwestią po prostu określania czasu plików binarnych.
Czy Twój lepszy plik binarny jest najszybszym plikiem binarnym, czy to ten, który najlepiej rekompensuje tanio spreparowany kod? A może najlepiej rekompensuje drogo spreparowany kod, który priorytetem jest łatwość konserwacji i ponowne wykorzystanie nad prędkością? Zależy to od charakteru i względnej wagi motywów tworzenia pliku binarnego oraz ograniczeń, na podstawie których to robisz.
W każdym razie, jeśli bardzo zależy ci na budowaniu „najlepszych” plików binarnych, lepiej sprawdzaj, w jaki sposób kolejne iteracje kompilatorów dostarczają ideę „najlepszej” nad kolejnymi iteracjami twojego kodu.
źródło
kcachegrind
do wskazania funkcji, w których generowane pliki wykonywalne różnią się wydajnością.Phoronix przeprowadził kilka testów w tym zakresie, ale chodzi o migawkową wersję Clang / LLVM sprzed kilku miesięcy. Rezultat jest taki, że rzeczy były mniej więcej popychaniem; ani GCC, ani Clang nie są zdecydowanie lepsze we wszystkich przypadkach.
Ponieważ użyjesz najnowszego Clanga, może to trochę mniej istotne. Z drugiej strony, GCC 4.6 prawdopodobnie ma kilka istotnych optymalizacji dla Core 2 i i7.
Myślę, że szybsza prędkość kompilacji Clanga będzie lepsza dla oryginalnych programistów, a następnie, gdy wypchniesz kod na świat, dystrybucja Linux / BSD / itp. użytkownicy końcowi będą używać GCC do szybszych plików binarnych.
źródło
Fakt, że Clang kompiluje kod szybciej, może nie być tak ważny jak szybkość wynikowego pliku binarnego. Oto jednak seria testów porównawczych .
źródło
Jest bardzo mała ogólna różnica między GCC 4.8 a clang 3.3 pod względem szybkości wynikowego pliku binarnego. W większości przypadków kod wygenerowany przez oba kompilatory działa podobnie. Żaden z tych dwóch kompilatorów nie dominuje nad drugim.
Benchmarki stwierdzające, że istnieje znaczna różnica w wydajności między GCC a klangiem, są przypadkowe.
Wybór kompilatora zależy od wydajności programu. Jeśli programista lub grupa programistów korzysta wyłącznie z GCC, można oczekiwać, że program będzie działał nieco szybciej z GCC niż z clang i odwrotnie.
Z punktu widzenia programisty zauważalna różnica między GCC 4.8+ a clang 3.3 polega na tym, że GCC ma
-Og
opcję wiersza poleceń. Ta opcja umożliwia optymalizacje, które nie zakłócają debugowania, więc na przykład zawsze można uzyskać dokładne ślady stosu. Brak tej opcji w clang sprawia, że trudniej jest używać clang jako kompilatora optymalizującego dla niektórych programistów.źródło
Jedynym sposobem na określenie tego jest wypróbowanie. FWIW Widziałem kilka naprawdę dobrych ulepszeń przy użyciu Apple LLVM gcc 4.2 w porównaniu ze zwykłym gcc 4.2 (dla kodu x86-64 z dość dużą ilością SSE), ale YMMV dla różnych baz kodu. Zakładając, że pracujesz z x86 / x86-64 i że naprawdę zależy ci na ostatnich kilku procentach, powinieneś również wypróbować ICC Intela, ponieważ często może to pobić gcc - możesz uzyskać 30-dniową licencję próbną z intel.com i spróbuj.
źródło
Szczególną różnicą, którą zauważyłem w gcc 5.2.1 i clang 3.6.2, jest to, że jeśli masz krytyczną pętlę, taką jak:
Następnie gcc podczas kompilacji z
-O3
lub-O2
spekulacyjnie rozwija pętlę osiem razy. Clang w ogóle go nie rozwinie. Dzięki próbom i błędom odkryłem, że w moim konkretnym przypadku z danymi mojego programu odpowiednia ilość rozwijania wynosi pięć, więc gcc jest przekroczony i kliknął poniżej. Przekroczenie było jednak bardziej szkodliwe dla wyników, więc gcc działał tutaj znacznie gorzej.Nie mam pojęcia, czy różnica w rozwijaniu jest ogólnym trendem, czy tylko czymś specyficznym dla mojego scenariusza.
Jakiś czas temu napisałem kilka śmieciarek, aby nauczyć się więcej na temat optymalizacji wydajności w C. A wyniki, które uzyskałem, są w mojej głowie na tyle, aby lekko sprzyjać brzęczeniu. Zwłaszcza, że odśmiecanie polega głównie na ściganiu wskaźnika i kopiowaniu pamięci.
Wyniki są następujące (liczby w sekundach):
To wszystko jest czystym kodem C i nie twierdzę, że którykolwiek z kompilatorów działa podczas kompilowania kodu C ++.
Na Ubuntu 15.10, x86.64 i procesor AMD Phenom (tm) II X6 1090T.
źródło
Zasadniczo odpowiedź brzmi: to zależy. Istnieje wiele wielu testów porównawczych dotyczących różnych rodzajów aplikacji.
Mój test porównawczy w mojej aplikacji to: gcc> icc> clang.
Istnieją rzadkie operacje wejścia / wyjścia, ale wiele operacji zmiennoprzecinkowych i operacji na strukturze danych.
flagi kompilacji to -Wall -g -DNDEBUG -O3.
https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark
źródło