Clang vs GCC - che produce binari migliori? [chiuso]


238

Attualmente sto usando GCC, ma ho scoperto Clang di recente e sto riflettendo sul passaggio. Tuttavia, esiste un fattore decisivo: la qualità (velocità, footprint di memoria, affidabilità) dei file binari che produce - se è in gcc -O3grado di produrre un file binario che esegue l'1% più velocemente o occupa l'1% di memoria in meno, è un problema.

Clang vanta velocità di compilazione migliori e un ingombro della memoria in fase di compilazione inferiore rispetto a GCC, ma sono davvero interessato ai benchmark / confronti del software compilato risultante - potresti indicarmi alcuni o descrivere le tue esperienze?


5
Sembra ancora una domanda e risposte preziose, e molti sono interessati.
YasserAsmi,

9
@YasserAsmi: E le due metriche - footprint di memoria e velocità di esecuzione - sono tutt'altro che arbitrarie o soggette a "opinioni". Ma sembra che la malattia di Physics.SE si diffonda qui e la gente abbia iniziato a votare per chiudere senza leggere qui i dettagli del testo delle domande.
SF.

12
la domanda richiede benchmark e confronti, la risposta dà entrambi ... perché questa opinione è invece del confronto fattuale?
oemb1905,

2
Non vedo perché questa domanda è stata chiusa. Che si tratti di opinioni o fatti, vogliamo conoscere la risposta e contrassegnarla come chiusa le dà una sfumatura negativa, dove non dovrebbe essercene una.
Timothy Makobu,

2
@TomZych: se vuoi conoscere questi fattori, fai diverse domande. Questo è molto specifico e inequivocabile: richiede velocità di esecuzione e ingombro di memoria. Potresti essere interessato ad altri fattori, buoni per te, ciò non significa che questa domanda non sia valida, semplicemente non soddisfa i tuoi interessi personali. È come se fossi un programmatore Java e desideri chiudere ogni domanda C # perché non parla di Java.
SF.

Risposte:


239

Ecco alcuni dei miei dati aggiornati, sebbene ristretti, con GCC 4.7.2 e Clang 3.2 per C ++.

AGGIORNAMENTO: confronto GCC 4.8.1 v clang 3.3 allegato di seguito.

AGGIORNAMENTO: il confronto GCC 4.8.2 v clang 3.4 viene aggiunto a questo.

Mantengo uno strumento OSS creato per Linux con GCC e Clang e con il compilatore di Microsoft per Windows. Lo strumento, coan, è un preprocessore e analizzatore di file sorgente C / C ++ e codeline di tali: il suo profilo computazionale si specializza in analisi ricorsive-discendenza e gestione dei file. Il ramo di sviluppo (a cui appartengono questi risultati) comprende attualmente circa 11K LOC in circa 90 file. È codificato, ora, in C ++ che è ricco di polimorfismo e modelli e, ma è ancora impantanato in molte patch dal suo passato non così lontano nel C. unito La semantica non viene espressamente sfruttata. È a thread singolo. Non ho dedicato alcuno sforzo per ottimizzarlo, mentre l '"architettura" rimane così ampiamente da fare.

Ho usato Clang prima della 3.2 solo come compilatore sperimentale perché, nonostante la sua velocità di compilazione e diagnostica superiori, il suo supporto standard C ++ 11 era in ritardo rispetto alla versione GCC contemporanea sotto gli aspetti esercitati da Coan. Con 3.2, questo divario è stato chiuso.

Il mio cablaggio di test Linux per gli attuali processi di sviluppo di Coan elabora circa 70.000 file di sorgenti in una combinazione di casi di test del parser a un file, stress test che consumano migliaia di file e test di scenario che consumano <1 KB di file. Oltre a riportare i risultati del test, il cablaggio si accumula e visualizza i totali dei file consumati e il tempo di esecuzione consumato in Coan (passa semplicemente ogni riga di comando Coan al comando Linux timee acquisisce e somma i numeri riportati). I tempi sono lusingati dal fatto che qualsiasi numero di test che impiegano 0 tempo misurabile si sommerà a 0, ma il contributo di tali test è trascurabile. Le statistiche dei tempi sono visualizzate alla fine in make checkquesto modo:

coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.

Ho confrontato le prestazioni del cablaggio di prova tra GCC 4.7.2 e Clang 3.2, a parità di condizioni tranne i compilatori. A partire da Clang 3.2, non ho più bisogno di alcuna differenziazione del preprocessore tra tratti di codice che GCC compilerà e alternative di Clang. Ho creato la stessa libreria C ++ (GCC) in ogni caso e ho eseguito tutti i confronti consecutivamente nella stessa sessione terminale.

Il livello di ottimizzazione predefinito per la mia build di rilascio è -O2. Ho anche testato con successo build a -O3. Ho testato ogni configurazione 3 volte back-to-back e ho mediato i 3 risultati, con i seguenti risultati. Il numero in una cella di dati è il numero medio di microsecondi consumati dall'eseguibile coan per elaborare ciascuno dei file di input ~ 70K (lettura, analisi e scrittura dell'output e della diagnostica).

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|

È molto probabile che ogni particolare applicazione abbia tratti che giocano ingiustamente ai punti di forza o di debolezza di un compilatore. Il benchmarking rigoroso impiega diverse applicazioni. Tenendo ben presente ciò, le caratteristiche degne di nota di questi dati sono:

  1. -L'ottimizzazione dell'O3 è stata leggermente dannosa per GCC
  2. -L'ottimizzazione dell'O3 è stata molto utile per Clang
  3. Con l'ottimizzazione di -O2, GCC è stato più veloce di Clang con un semplice baffo
  4. Con l'ottimizzazione -O3, Clang era molto più veloce di GCC.

Un ulteriore interessante confronto tra i due compilatori è emerso per caso poco dopo questi risultati. Coan impiega generosamente puntatori intelligenti e uno di questi è fortemente esercitato nella gestione dei file. Questo particolare tipo di puntatore intelligente era stato digitato in versioni precedenti per motivi di differenziazione del compilatore, per essere un std::unique_ptr<X>se il compilatore configurato avesse un supporto sufficientemente maturo per il suo utilizzo come quello, e altrimenti un std::shared_ptr<X>. L'inclinazione è std::unique_ptrstata insensata, dal momento che questi puntatori erano in effetti trasferiti in giro, ma std::unique_ptrsembrava l'opzione più adatta per la sostituzione std::auto_ptrin un momento in cui le varianti di C ++ 11 erano nuove per me.

Nel corso di build sperimentali per valutare la continua necessità di Clang 3.2 di questa e di una simile differenziazione, ho inavvertitamente costruito std::shared_ptr<X>quando avevo intenzione di costruire std::unique_ptr<X>, e sono stato sorpreso di osservare che l'eseguibile risultante, con ottimizzazione predefinita -O2, era il più veloce che io aveva visto, a volte raggiungendo 184 msec. per file di input. Con questa modifica al codice sorgente, i risultati corrispondenti sono stati questi;

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |

I punti di nota qui sono:

  1. Nessuno dei due compilatori ora beneficia affatto dell'ottimizzazione -O3.
  2. Clang batte GCC altrettanto importante per ogni livello di ottimizzazione.
  3. Le prestazioni di GCC sono influenzate solo marginalmente dalla modifica del tipo di puntatore intelligente.
  4. Le prestazioni di Clang -O2 sono influenzate in modo significativo dalla modifica del tipo di puntatore intelligente.

Prima e dopo la modifica del tipo di puntatore intelligente, Clang è in grado di creare un eseguibile coan sostanzialmente più veloce con ottimizzazione -O3 e può creare un eseguibile ugualmente più veloce con -O2 e -O3 quando quel tipo di puntatore è il migliore - std::shared_ptr<X>- per il lavoro.

Una domanda ovvia su cui non sono competente a commentare è perché Clang dovrebbe essere in grado di trovare una velocità del 25% -O2 nella mia applicazione quando un tipo di puntatore intelligente molto usato viene cambiato da unico a condiviso, mentre GCC è indifferente allo stesso cambiamento. Né so se dovrei esultare o rincuorare la scoperta che l'ottimizzazione -O2 di Clang nasconde una sensibilità così grande alla saggezza delle mie scelte di puntatore intelligente.

AGGIORNAMENTO: GCC 4.8.1 v clang 3.3

I risultati corrispondenti ora sono:

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |

Il fatto che ora tutti e quattro i file eseguibili impieghino un tempo medio molto più lungo rispetto a prima per elaborare il file 1 non riflette le prestazioni degli ultimi compilatori. È dovuto al fatto che il ramo di sviluppo successivo dell'applicazione di test ha assunto nel frattempo molta raffinatezza di analisi e la paga rapidamente. Solo i rapporti sono significativi.

I punti di nota ora non sono in modo accattivante:

  • GCC è indifferente all'ottimizzazione -O3
  • clang beneficia molto marginalmente dell'ottimizzazione -O3
  • clang batte GCC con un margine altrettanto importante ad ogni livello di ottimizzazione.

Confrontando questi risultati con quelli di GCC 4.7.2 e clang 3.2, si nota che GCC ha recuperato circa un quarto del vantaggio di clang ad ogni livello di ottimizzazione. Ma dal momento che l'applicazione di test è stata fortemente sviluppata nel frattempo, non si può attribuire con sicurezza questo a un recupero nella generazione del codice di GCC. (Questa volta, ho notato l'istantanea dell'applicazione da cui sono stati ottenuti i tempi e posso riutilizzarla.)

AGGIORNAMENTO: GCC 4.8.2 v clang 3.4

Ho terminato l'aggiornamento per GCC 4.8.1 v Clang 3.3 dicendo che mi sarei attaccato alla stessa fotografia istantanea per ulteriori aggiornamenti. Ma ho deciso invece di provare su quella istantanea (rev. 301) e sull'ultima istantanea di sviluppo che ho superato la sua suite di test (rev. 619). Questo dà ai risultati un po 'di longitudine e ho avuto un altro motivo:

Il mio post originale ha notato che non avevo dedicato alcuno sforzo all'ottimizzazione della velocità per la navigazione. Questo era ancora il caso del rev. 301. Tuttavia, dopo aver incorporato l'apparato di cronometraggio nell'imbracatura del test di controllo, ogni volta che ho eseguito la suite di test, l'impatto sulle prestazioni degli ultimi cambiamenti mi ha fissato in faccia. Ho visto che spesso era sorprendentemente grande e che la tendenza era molto più negativa di quanto mi sentissi meritato dai guadagni in termini di funzionalità.

Di rev. 308 il tempo medio di elaborazione per file di input nella suite di test era ben più che raddoppiato dalla prima pubblicazione qui. A quel punto ho fatto un'inversione di marcia sulla mia politica decennale di non preoccuparmi delle prestazioni. Nell'ondata intensa di revisioni fino a 619 prestazioni è sempre stata una considerazione e un gran numero di loro è andato semplicemente a riscrivere i portatori di carico chiave su linee fondamentalmente più veloci (anche se senza usare funzioni di compilatore non standard per farlo). Sarebbe interessante vedere la reazione di ciascun compilatore a questa inversione a U,

Ecco la matrice dei tempi ormai familiare per le ultime due build di rev.301 dei compilatori:

coan - rev.301 risultati

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|

La storia qui è cambiata solo marginalmente da GCC-4.8.1 e Clang-3.3. La proiezione di GCC è un po 'meglio. Clang è un po 'peggio. Il rumore potrebbe giustificare questo. Clang esce ancora avanti -O2e -O3margini che non importerebbero nella maggior parte delle applicazioni ma ne conterebbero parecchi.

Ed ecco la matrice per il rev. 619.

coan - rev.619 risultati

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|

Accostando le figure 301 e 619, parecchi punti parlano chiaro.

  • Avevo l'obiettivo di scrivere un codice più veloce ed entrambi i compilatori confermano con enfasi i miei sforzi. Ma:

  • GCC ripaga quegli sforzi molto più generosamente di Clang. Al -O2 ottimizzazione di Clang 619 build è 46% più veloce rispetto al suo 301 costruire: a -O3miglioramento di Clang è del 31%. Bene, ma a ogni livello di ottimizzazione la build 619 di GCC è più del doppio della sua 301.

  • GCC più che inverte la precedente superiorità di Clang. E ad ogni livello di ottimizzazione GCC ora batte Clang del 17%.

  • La capacità di Clang nella build 301 di ottenere più leva di GCC -O3dall'ottimizzazione è sparita nella build 619. Nessuno dei due compilatori guadagna significativamente da -O3.

Sono stato sufficientemente sorpreso da questo capovolgimento di fortune che sospettavo di aver potuto accidentalmente creare una costruzione lenta dello stesso clang 3.4 (da quando l'ho costruito dalla fonte). Quindi ho eseguito nuovamente il test 619 con il Clang 3.3 di serie della mia distribuzione. I risultati erano praticamente gli stessi di 3.4.

Quindi per quanto riguarda la reazione all'inversione a U: sui numeri qui, Clang ha fatto molto meglio di GCC a sottrarre velocità dal mio codice C ++ quando non gli ho dato alcun aiuto. Quando ho deciso di aiutare, GCC ha fatto un lavoro molto migliore di Clang.

Non elevo tale osservazione in un principio, ma prendo la lezione che "Quale compilatore produce i binari migliori?" è una domanda che, anche se si specifica la suite di test a cui la risposta deve essere relativa, non è ancora una questione netta di temporizzazione dei binari.

Il tuo binario migliore è il binario più veloce o è quello che meglio compensa il codice a basso costo? O meglio compensa il codice costoso che dà priorità alla manutenibilità e al riutilizzo rispetto alla velocità? Dipende dalla natura e dai pesi relativi dei tuoi motivi per produrre il binario e dai vincoli sotto i quali lo fai.

E in ogni caso, se ti preoccupi profondamente di costruire i binari "migliori", allora è meglio che tu continui a controllare come le iterazioni successive di compilatori trasmettono la tua idea di "il migliore" rispetto alle successive iterazioni del tuo codice.


9
perché il clang è più veloce? ad esempio, il compilatore Intel utilizzava specialità di chip Intel. cosa sta usando clang per ottenere un vantaggio? il codice può essere riscritto in modo che gcc abbia le stesse prestazioni?
kirill_igum,

27
@krill_igum GCC e clang sono programmi diversi (enormemente complessi) scritti da diversi gruppi di programmatori per fare lo stesso lavoro: tradurre il codice sorgente in codice oggetto. È quasi inevitabile che uno di loro svolga quel lavoro in modo misurabile meglio dell'altro in qualsiasi test scelto in qualsiasi momento. Non ci deve essere alcuna "cosa" speciale che il vincitore stia "usando" per "ottenere un vantaggio" e poiché entrambi i programmi sono open-source non hanno segreti l'uno dell'altro.
Mike Kinghan,

3
È possibile utilizzare kcachegrindper individuare le funzioni in cui gli eseguibili generati differiscono nelle prestazioni.

4
-1: Questo è più un romanzo (o un post sul blog) che una risposta.
John Saunders,

60
@JohnSaunders: Ciò che per una persona è una risposta dettagliata e approfondita, per un'altra è un romanzo indegno della loro attenzione. Dimmi cosa distingue queste due persone.
SF.

48

Phoronix ha fatto alcuni benchmark su questo, ma si tratta di una versione istantanea di Clang / LLVM di alcuni mesi fa. Il risultato è che le cose sono state più o meno una spinta; né GCC né Clang sono decisamente migliori in tutti i casi.

Dal momento che useresti l'ultimo Clang, forse è un po 'meno rilevante. Poi di nuovo, GCC 4.6 dovrebbe avere alcune importanti ottimizzazioni per Core 2 e i7, a quanto pare.

Immagino che la maggiore velocità di compilazione di Clang sarà migliore per gli sviluppatori originali, e quindi quando spingi il codice nel mondo, Linux distro / BSD / etc. gli utenti finali useranno GCC per i file binari più veloci.


2
Proprio oggi ho eseguito alcuni benchmark sulla velocità di compilazione di Clang ed è molto deludente per il puro C. La compilazione di file 35 C con clang 270 KLOC è stata solo del 25% più veloce. Quando vedo quanto è veloce tinycc su Linux è un brutto risultato per un nuovo compilatore scritto. Migliora quando si utilizzano le ottimizzazioni -O2 / -O3, ma poiché vengono utilizzate per la versione di rilascio, le prestazioni del compilatore non contano in questi casi.
Lothar,

7
@mcandre Forse Nietzche-jou è stato compilato con Clang, mentre tu eri compilato con GCC.
Mateen Ulhaq,

18

Il fatto che Clang compili il codice più velocemente potrebbe non essere importante quanto la velocità del binario risultante. Tuttavia, ecco una serie di parametri di riferimento .


12
In realtà lo fa. Durante lo sviluppo, i tempi di compilazione (e il consumo di risorse dovuti alla compilazione) sono molto più un collo di bottiglia rispetto alle prestazioni binarie. Dopotutto, in questa fase compiliamo in modalità Debug. È solo quando arriva il palco per testare e spedire che si passa alla modalità di rilascio e si cerca di ottenere un binario il più rapidamente possibile.
Matthieu M.

3
@ Matthieu M: giuro che la risposta diceva "potrebbe ..", come se stesse sollevando una potenziale preoccupazione. Immagino che forse valesse la pena menzionarlo perché era, sai, correlato all'OP.
JM Becker,

D'accordo, anche se tutti i punti positivi qui. Preferirei inserire una seconda o terza unità RAID 0, un SSD o una RAM più veloce e più veloce e ottenere le migliori prestazioni .exe, a condizione che queste misure possano portarti alla parità o alla chiusura. A volte è anche utile sviluppare con più di un compilatore. Può renderti consapevole delle funzionalità non portatili, E rilevare errori che altrimenti non verrebbero rilevati, o portare a giorni di tempo sprecato nel tentativo di eseguire il debug del codice che un compilatore migliore avrebbe avvertito / errato.

Ho provato oggi a confrontare alcuni codici interi critici con prestazioni ridotte che ho scritto e GCC ha funzionato molto più velocemente (22S clang-llvm 25S) usando sia -O2 che -O3. Pensa che usare gli switch del compilatore (gcc o clang) copra la maggior parte delle funzionalità non standard e degli avvisi statici. Nel tuo grande progetto, non compilando in batch il codice di altri ppl, stai facendo qualcosa di sbagliato nel tuo sistema di compilazione se il tempo di compilazione domina il tempo di collegamento. Ci sono strumenti come ccache.samba.org che ti aiutano a pulire spesso. Un altro problema relativo alla modifica dei compilatori è l'investimento continuo in test / convalida che viene eliminato.
Rob11311,

code.google.com/p/distcc è un altro progetto che può accelerare i tempi di compilazione in blocco, se un'intera libreria deve essere ricompilata a causa di modifiche alla struttura dei dati o per scopi di verifica / convalida
Rob11311,

11

C'è una differenza globale molto piccola tra GCC 4.8 e clang 3.3 in termini di velocità del binario risultante. Nella maggior parte dei casi, il codice generato da entrambi i compilatori funziona in modo simile. Nessuno di questi due compilatori domina l'altro.

I benchmark che affermano che esiste un divario significativo nelle prestazioni tra GCC e clang sono casuali.

Le prestazioni del programma sono influenzate dalla scelta del compilatore. Se uno sviluppatore o un gruppo di sviluppatori utilizza esclusivamente GCC, è possibile che il programma funzioni leggermente più velocemente con GCC che con clang e viceversa.

Dal punto di vista degli sviluppatori, una notevole differenza tra GCC 4.8+ e clang 3.3 è che GCC ha l' -Ogopzione della riga di comando. Questa opzione abilita ottimizzazioni che non interferiscono con il debug, quindi ad esempio è sempre possibile ottenere tracce dello stack accurate. L'assenza di questa opzione in clang rende clang più difficile da usare come compilatore ottimizzante per alcuni sviluppatori.


Ultimamente, (3.3 e 4.8) non vedo molta differenza tra i tempi di compilazione. (nei "miei" programmi con tempi di compilazione compresi tra 10 secondi e 30 secondi).
alfC

9

L'unico modo per determinarlo è provarlo. FWIW Ho visto alcuni miglioramenti davvero buoni usando LLVM gcc 4.2 di Apple rispetto al normale gcc 4.2 (per codice x86-64 con un bel po 'di SSE), ma YMMV per diverse basi di codice. Supponendo che tu stia lavorando con x86 / x86-64 e che ti interessi davvero all'ultimo percento, allora dovresti provare anche l'ICC di Intel, poiché questo può spesso battere gcc - puoi ottenere una licenza di valutazione di 30 giorni da intel.com e provalo.


8

Una differenza peculiare che ho notato su gcc 5.2.1 e clang 3.6.2 è che se hai un ciclo critico come:

for (;;) {
    if (!visited) {
        ....
    }
    node++;
    if (!*node) break;
  }

Quindi gcc, durante la compilazione con -O3o -O2, srotolerà speculativamente il ciclo otto volte. Clang non lo srotolerà affatto. Attraverso prove ed errori ho scoperto che nel mio caso specifico con i dati del mio programma, la giusta quantità di srotolamento è cinque, quindi gcc overshot e clang undershot. Tuttavia, il surriscaldamento è stato più dannoso per le prestazioni, quindi gcc ha ottenuto risultati molto peggiori qui.

Non ho idea se la differenza di srotolamento sia una tendenza generale o solo qualcosa che fosse specifico per il mio scenario.

Qualche tempo fa ho scritto alcuni garbage collector per insegnarmi di più sull'ottimizzazione delle prestazioni in C. E i risultati che ho ottenuto sono nella mia mente abbastanza da favorire leggermente il clang. Soprattutto perché la garbage collection riguarda principalmente la ricerca dei puntatori e la copia della memoria.

I risultati sono (numeri in secondi):

+---------------------+-----+-----+
|Type                 |GCC  |Clang|
+---------------------+-----+-----+
|Copying GC           |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep         | 8.72| 8.38|
|Ref Counting/Cycles  |15.14|14.49|
|Ref Counting/Plain   | 9.94| 9.32|
+---------------------+-----+-----+

Questo è tutto puro codice C e non faccio affermazioni sulle prestazioni dei compilatori durante la compilazione del codice C ++.

Su Ubuntu 15.10, x86.64 e un processore AMD Phenom (tm) II X6 1090T.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.