Clang vs GCC - o que produz melhores binários? [fechadas]


238

Atualmente, estou usando o GCC, mas descobri o Clang recentemente e estou pensando em mudar. No entanto, há um fator decisivo - qualidade (velocidade, pegada de memória, confiabilidade) dos binários que produz - se é gcc -O3possível produzir um binário que corra 1% mais rápido ou consome 1% menos memória, é um diferencial.

O Clang possui melhores velocidades de compilação e menor presença de memória em tempo de compilação que o GCC, mas estou realmente interessado em benchmarks / comparações de softwares compilados resultantes - você poderia me indicar algumas ou descrever suas experiências?


5
Ainda parece ser uma pergunta e respostas valiosas, e muitas estão interessadas.
YasserAsmi

9
@YasserAsmi: E as duas métricas - pegada de memória e velocidade de execução - estão longe de serem arbitrárias ou estão sujeitas a "opinião". Mas parece que a doença da Physics.SE se espalha aqui e as pessoas começaram a votar para fechar sem ler os detalhes do texto da pergunta aqui também.
SF.

12
a pergunta pede benchmarks e comparações, a resposta fornece ambos ... por que essa opinião é em vez de comparação factual?
oemb1905

2
Não veja por que essa pergunta foi encerrada. Quer se trate de opinião ou fato, queremos saber a resposta e marcá-la como fechada dá um tom negativo, onde não deveria haver um.
Timothy Makobu

2
@ TomZych: Se você deseja conhecer esses fatores, faça perguntas diferentes. Este é muito específico e inequívoco - solicitando velocidade de execução e pegada de memória. Você pode estar interessado em outros fatores, que são bons para você, que não significam que esta pergunta é inválida, apenas que não atende aos seus interesses pessoais. É como se você fosse um programador Java e desejasse fechar todas as questões de C # porque não fala sobre Java.
SF.

Respostas:


239

Aqui estão algumas descobertas minhas atualizadas, embora limitadas, com o GCC 4.7.2 e o Clang 3.2 para C ++.

ATUALIZAÇÃO: comparação do GCC 4.8.1 v clang 3.3 anexada abaixo.

ATUALIZAÇÃO: A comparação do GCC 4.8.2 v clang 3.4 é anexada a isso.

Eu mantenho uma ferramenta OSS criada para Linux com GCC e Clang e com o compilador da Microsoft para Windows. A ferramenta, coan, é um pré-processador e analisador de arquivos de origem C / C ++ e linhas de código de tais: seu perfil computacional é mais importante na análise e manipulação de arquivos de descida recursiva. O ramo de desenvolvimento (ao qual esses resultados se referem) compreende atualmente cerca de 11K LOC em cerca de 90 arquivos. Agora, ele é codificado em C ++, rico em polimorfismo e modelos, mas ainda está envolvido em muitos patches por seu passado não tão distante no C. hackeado em conjunto. A semântica de movimentos não é expressamente explorada. É de rosca única. Não dediquei nenhum esforço sério a otimizá-lo, enquanto a "arquitetura" permanece tão amplamente relacionada à tarefa.

Empreguei o Clang anterior ao 3.2 apenas como um compilador experimental porque, apesar de sua velocidade e diagnóstico de compilação superiores, seu suporte padrão C ++ 11 ficou aquém da versão contemporânea do GCC nos aspectos exercidos pelo coan. Com o 3.2, essa lacuna foi fechada.

O equipamento de teste do meu Linux para o desenvolvimento de coan atual processa aproximadamente 70K arquivos de origem em uma mistura de casos de teste do analisador de um arquivo, testes de estresse que consomem 1000s de arquivos e testes de cenário que consomem <1K arquivos. Além de relatar os resultados do teste, o chicote de fios acumula e exibe os totais de arquivos consumidos e o tempo de execução consumido em coan (apenas passa cada linha de comando coan para o timecomando Linux, captura e soma os números relatados). Os tempos são lisonjeados pelo fato de que qualquer número de testes que levam 0 tempo mensurável irá somar 0, mas a contribuição desses testes é insignificante. As estatísticas de tempo são exibidas no final make checkdesta forma:

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.

Comparei o desempenho do equipamento de teste entre o GCC 4.7.2 e o Clang 3.2, todos iguais, exceto os compiladores. No Clang 3.2, não era mais necessária nenhuma diferenciação de pré-processador entre os trechos de código que o GCC compilaria e as alternativas do Clang. Criei na mesma biblioteca C ++ (GCC) em cada caso e executei todas as comparações consecutivamente na mesma sessão do terminal.

O nível de otimização padrão para minha versão é -O2. Também testei com êxito compilações em -O3. Testei cada configuração três vezes consecutivas e calculei a média dos três resultados, com os seguintes resultados. O número em uma célula de dados é o número médio de microssegundos consumidos pelo executável coan para processar cada um dos ~ 70K arquivos de entrada (leitura, análise e saída e diagnóstico de gravação).

          | -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|

É provável que qualquer aplicativo em particular tenha características que sejam injustas com os pontos fortes ou fracos de um compilador. O benchmarking rigoroso emprega diversas aplicações. Com isso em mente, as principais características desses dados são:

  1. A otimização de OO foi marginalmente prejudicial ao GCC
  2. Otimização de OO foi importante para Clang
  3. Na otimização de -O2, o GCC foi mais rápido que Clang com apenas um bigode
  4. Na otimização de -O3, o Clang era importante mais rápido que o GCC.

Uma comparação interessante adicional dos dois compiladores surgiu por acidente logo após essas descobertas. Coan liberalmente emprega ponteiros inteligentes e um deles é muito exercido no manuseio de arquivos. Esse tipo de ponteiro inteligente em particular havia sido digitado em versões anteriores para fins de diferenciação do compilador, para ser um std::unique_ptr<X>se o compilador configurado tiver suporte suficientemente maduro para seu uso como esse e, caso contrário, um std::shared_ptr<X>. O viés para std::unique_ptrera tolo, já que esses ponteiros eram de fato transferidos, mas std::unique_ptrparecia a opção mais adequada para substituir std::auto_ptrem um ponto em que as variantes do C ++ 11 eram novas para mim.

No decorrer de compilações experimentais para avaliar a necessidade contínua do Clang 3.2 por essa diferenciação e semelhante, construí inadvertidamente std::shared_ptr<X>quando pretendia criar std::unique_ptr<X>e fiquei surpreso ao observar que o executável resultante, com otimização -O2 padrão, foi o mais rápido que eu tinha visto, às vezes atingindo 184 ms. por arquivo de entrada. Com essa alteração no código fonte, os resultados correspondentes foram estes;

          | -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 |

Os pontos de observação aqui são:

  1. Agora nenhum compilador se beneficia da otimização -O3.
  2. O clang é melhor do que o GCC em cada nível de otimização.
  3. O desempenho do GCC é afetado apenas marginalmente pela alteração do tipo de ponteiro inteligente.
  4. O desempenho de -O2 de Clang é afetado de maneira importante pela alteração do tipo de ponteiro inteligente.

Antes e depois da alteração do tipo de ponteiro inteligente, o Clang pode criar um executável de bobina substancialmente mais rápido na otimização de -O3 e pode criar um executável igualmente mais rápido em -O2 e -O3 quando esse tipo de ponteiro é o melhor - std::shared_ptr<X>- para o trabalho.

Uma pergunta óbvia que não sou competente para comentar é por que o Clang deve conseguir uma velocidade de 25% -O2 no meu aplicativo quando um tipo de ponteiro inteligente muito usado é alterado de exclusivo para compartilhado, enquanto o GCC é indiferente para a mesma mudança. Tampouco sei se devo torcer ou vaiar a descoberta de que a otimização -O2 de Clang abriga uma sensibilidade tão grande à sabedoria de minhas escolhas de ponteiros inteligentes.

ATUALIZAÇÃO: GCC 4.8.1 v clang 3.3

Os resultados correspondentes agora são:

          | -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 |

O fato de todos os quatro executáveis ​​agora levarem um tempo médio muito maior do que o anteriormente para processar o arquivo 1 não reflete no desempenho dos compiladores mais recentes. Isso se deve ao fato de que o ramo de desenvolvimento posterior do aplicativo de teste adquiriu muita sofisticação de análise nesse meio tempo e paga por isso rapidamente. Somente as proporções são significativas.

Os pontos de observação agora não são surpreendentemente novos:

  • O GCC é indiferente à otimização de -O3
  • clang se beneficia muito marginalmente da otimização de -O3
  • O clang supera o GCC por uma margem igualmente importante em cada nível de otimização.

Comparando esses resultados com os do GCC 4.7.2 e do clang 3.2, destaca-se que o GCC recuperou cerca de um quarto da liderança do clang em cada nível de otimização. Porém, como o aplicativo de teste foi bastante desenvolvido nesse meio tempo, não se pode atribuir isso com confiança a uma atualização na geração de código do GCC. (Dessa vez, observei o instantâneo do aplicativo a partir do qual os tempos foram obtidos e posso usá-lo novamente.)

ATUALIZAÇÃO: GCC 4.8.2 v 3.4

Concluí a atualização para o GCC 4.8.1 v Clang 3.3 dizendo que continuaria com o mesmo instantâneo coan para obter mais atualizações. Mas, em vez disso, decidi testar nesse instantâneo (rev. 301) e no último instantâneo de desenvolvimento que tenho que passa em seu conjunto de testes (rev. 619). Isso dá aos resultados um pouco de longitude, e eu tive outro motivo:

Minha postagem original observou que eu não havia me esforçado para otimizar a velocidade da bobina. Este ainda era o caso da rev. 301. No entanto, depois de montar o aparelho de temporização no equipamento de teste de coan, toda vez que eu executava o conjunto de testes, o impacto no desempenho das alterações mais recentes me encarava. Vi que muitas vezes era surpreendentemente grande e que a tendência era mais acentuadamente negativa do que me pareceu merecida por ganhos de funcionalidade.

Pela rev. 308, o tempo médio de processamento por arquivo de entrada no conjunto de testes quase dobrou desde a primeira postagem aqui. Naquele momento, revirei minha política de 10 anos de não me preocupar com o desempenho. Na série intensiva de revisões, o desempenho de 619 sempre foi considerado, e um grande número delas foi exclusivamente para reescrever os principais porta-cargas em linhas fundamentalmente mais rápidas (embora sem o uso de recursos não-padrão do compilador para fazê-lo). Seria interessante ver a reação de cada compilador a essa inversão de marcha,

Aqui está a matriz de tempos agora familiar para as versões mais recentes dos dois compiladores da rev.301:

coan - rev.301 resultados

          | -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|

A história aqui é apenas marginalmente alterada de GCC-4.8.1 e Clang-3.3. A exibição do GCC é um pouco melhor. O de Clang é um pouco pior. O barulho poderia explicar isso. O Clang ainda se destaca -O2e -O3margens que não importariam na maioria das aplicações, mas importariam para algumas.

E aqui está a matriz para rev. 619

coan - rev.619 resultados

          | -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|

Tomando as figuras 301 e 619 lado a lado, vários pontos se manifestam.

  • Eu estava com o objetivo de escrever código mais rápido, e ambos os compiladores justificam enfaticamente meus esforços. Mas:

  • O GCC retribui esses esforços com muito mais generosidade do que Clang. Na -O2 otimização, a compilação 619 da Clang é 46% mais rápida que a compilação 301: na -O3melhoria da Clang, 31%. Bom, mas em cada nível de otimização, a compilação 619 do GCC é duas vezes mais rápida que a 301.

  • O GCC mais do que inverte a anterior superioridade de Clang. E em cada nível de otimização, o GCC agora supera o Clang em 17%.

  • A capacidade de Clang na compilação 301 de obter mais alavancagem do que o GCC da -O3otimização desapareceu na compilação 619. Nenhum compilador ganha significativamente de -O3.

Fiquei suficientemente surpreso com essa inversão de fortunas que suspeitei ter feito acidentalmente uma compilação lenta do próprio clang 3.4 (desde que o construí da fonte). Então, refiz o teste 619 com o estoque da minha distribuição Clang 3.3. Os resultados foram praticamente os mesmos que para 3.4.

Assim, no que diz respeito à reação à inversão de marcha: nos números aqui, Clang se saiu muito melhor que o GCC na velocidade de arrancar meu código C ++ quando eu não estava ajudando. Quando decidi ajudar, o GCC fez um trabalho muito melhor do que Clang.

Não elevo essa observação a um princípio, mas tomo a lição de que "Qual compilador produz os melhores binários?" é uma pergunta que, mesmo que você especifique o conjunto de testes ao qual a resposta deve ser relativa, ainda não é uma questão clara de apenas cronometrar os binários.

O seu melhor binário é o binário mais rápido ou é o que melhor compensa o código mais barato? Ou compensa melhor o código criado caro que prioriza a capacidade de manutenção e a reutilização em detrimento da velocidade? Depende da natureza e dos pesos relativos de seus motivos para produzir o binário e das restrições sob as quais você o faz.

De qualquer forma, se você se preocupa profundamente em criar os "melhores" binários, é melhor continuar verificando como as iterações sucessivas dos compiladores produzem sua idéia das "melhores" sobre as iterações sucessivas do seu código.


9
por que clang é mais rápido? por exemplo, o compilador intel usou especialidades de chips intel. o que o clang está usando para obter uma vantagem? o código pode ser reescrito para que o gcc tenha o mesmo desempenho?
kirill_igum

27
@krill_igum O GCC e o clang são programas diferentes (enormemente complexos) escritos por diferentes grupos de programadores para fazer o mesmo trabalho: converter o código-fonte em código-objeto. É quase inevitável que um deles faça esse trabalho de maneira mensurável melhor que o outro em qualquer teste escolhido a qualquer momento. Não precisa haver nenhuma "coisa" especial que o vencedor esteja "usando" para "obter uma vantagem", e como os dois programas são de código aberto, não têm segredos um do outro.
Mike Kinghan

3
É possível usar kcachegrindpara identificar as funções em que os executáveis ​​gerados diferem no desempenho.

4
-1: isso é mais um romance (ou postagem de blog) do que uma resposta.
John Saunders

60
@ JohnSaunders: O que para uma pessoa é uma resposta detalhada e profunda, para outra é um romance indigno de sua atenção. Diga-me o que diferencia essas duas pessoas.
SF.

48

O Phoronix fez alguns benchmarks sobre isso, mas trata-se de uma versão instantânea do Clang / LLVM de alguns meses atrás. Os resultados são que as coisas foram mais ou menos um empurrão; nem o GCC nem o Clang são definitivamente melhores em todos os casos.

Como você usaria o Clang mais recente, talvez seja um pouco menos relevante. Por outro lado, o GCC 4.6 está programado para ter algumas otimizações importantes para o Core 2 e i7, aparentemente.

Eu acho que a velocidade de compilação mais rápida do Clang será melhor para os desenvolvedores originais e, quando você envia o código ao mundo, a distribuição Linux / BSD / etc. os usuários finais usarão o GCC para os binários mais rápidos.


2
Hoje, eu corro alguns benchmarks na velocidade de compilação do Clang e é muito decepcionante para o C. puro. A compilação de arquivos 35 C com clang 270 KLOC foi apenas 25% mais rápida. Quando vejo quão rápido o tinycc está no linux, é um mau resultado para um novo compilador escrito. Fica melhor ao usar as otimizações -O2 / -O3, mas como elas são usadas para compilar a versão, o desempenho do compilador não importa nesses casos.
Lothar

7
@mcandre Talvez Nietzche-jou tenha sido compilado com Clang, enquanto você foi compilado com o GCC.
Mateen Ulhaq 23/09/11

18

O fato de o Clang compilar o código mais rapidamente pode não ser tão importante quanto a velocidade do binário resultante. No entanto, aqui está uma série de referências .


12
Na verdade, sim. Durante o desenvolvimento, o tempo de compilação (e o consumo de recursos devido à compilação) são muito mais um gargalo do que o desempenho binário. Afinal, compilamos no modo Debug nesta fase. É somente quando chega o estágio para testar e enviar que você alterna para o modo Release e tenta obter o mais rápido possível um binário.
Matthieu M.

3
@ Matthieu M: Juro que a resposta dizia "pode ​​..", como se ele estivesse levantando uma preocupação em potencial. Acho que talvez valha a pena mencionar, porque estava relacionado ao OP.
JM Becker

Concordo, embora todos os bons pontos aqui. Prefiro usar uma segunda ou terceira unidade RAID 0, um SSD ou mais e mais RAM e obter o melhor desempenho .exe - desde que essa medida possa levá-lo à paridade ou ao fechamento. Às vezes, também é útil desenvolver com mais de um compilador. Ele pode alertá-lo sobre os recursos não portáteis e detectar erros que, de outra forma, não seriam detectados ou levar a dias de tempo perdido tentando depurar códigos que um compilador melhor teria alertado / errado.

Hoje tentei comparar alguns códigos inteiros críticos de desempenho apertado que escrevi e o GCC foi muito mais rápido (22S clang-llvm 25S) usando -O2 e -O3. Pense que o uso de opções do compilador (gcc ou clang) abrange a maioria dos recursos não padrão e avisos estáticos. Em seu próprio projeto grande, não compilando em lote o código de outras pessoas, você está fazendo algo errado no seu sistema de compilação se o tempo de compilação dominar o tempo do link. Existem ferramentas como o ccache.samba.org que ajudam se você limpar com frequência. Outro problema com a mudança de compiladores é o tempo todo de investimento em testes / validação que são descartados.
Rob11311

code.google.com/p/distcc é outro projeto que pode acelerar tempos de compilação em massa, se uma biblioteca inteira precisa recompilar devido a alterações de estrutura de dados, ou para verificação / validação fins
Rob11311

11

Há muito pouca diferença geral entre o GCC 4.8 e o clang 3.3 em termos de velocidade do binário resultante. Na maioria dos casos, o código gerado pelos dois compiladores tem um desempenho semelhante. Nenhum desses dois compiladores domina o outro.

Os benchmarks informando que há uma lacuna de desempenho significativa entre o GCC e o clang são coincidentes.

O desempenho do programa é afetado pela escolha do compilador. Se um desenvolvedor ou um grupo de desenvolvedores estiver usando exclusivamente o GCC, é esperado que o programa seja executado um pouco mais rápido com o GCC do que com o clang e vice-versa.

Do ponto de vista do desenvolvedor, uma diferença notável entre o GCC 4.8+ e o clang 3.3 é que o GCC tem a -Ogopção de linha de comando. Essa opção permite otimizações que não interferem na depuração, por exemplo, sempre é possível obter rastreamentos de pilha precisos. A ausência dessa opção no clang dificulta o uso do clang como um compilador otimizador para alguns desenvolvedores.


Ultimamente, (3.3 e 4.8), não vejo muita diferença entre o tempo de compilação. (nos programas "my" com tempos de compilação entre 10segundos e 30 segundos).
ALFC

9

A única maneira de determinar isso é tentar. FWIW: Vi algumas melhorias muito boas usando o LLVM gcc 4.2 da Apple em comparação com o gcc 4.2 normal (para código x86-64 com bastante SSE), mas o YMMV para diferentes bases de código. Supondo que você esteja trabalhando com x86 / x86-64 e que realmente se preocupa com os últimos por cento, também deve experimentar o ICC da Intel, pois isso pode superar o gcc - você pode obter uma licença de avaliação de 30 dias no site intel.com e tente.


8

Uma diferença peculiar que observei no gcc 5.2.1 e no clang 3.6.2 é que se você tiver um loop crítico como:

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

Então, o gcc, ao compilar com -O3ou -O2, desenrola especulativamente o loop oito vezes. Clang não vai desenrolá-lo. Por tentativa e erro, descobri que, no meu caso específico, com os dados do meu programa, a quantidade certa de desenrolamento é de cinco, de modo que o gcc ultrapassa e ultrapassa o resultado. No entanto, a ultrapassagem foi mais prejudicial para o desempenho, portanto o gcc teve um desempenho muito pior aqui.

Não tenho idéia se a diferença que se desenrola é uma tendência geral ou apenas algo que foi específico para o meu cenário.

Há algum tempo, escrevi alguns coletores de lixo para me ensinar mais sobre otimização de desempenho em C. E os resultados que obtive estão em minha mente o suficiente para favorecer um pouco o clang. Especialmente porque a coleta de lixo é principalmente sobre busca de ponteiros e cópia de memória.

Os resultados são (números em segundos):

+---------------------+-----+-----+
|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|
+---------------------+-----+-----+

Isso é tudo código C puro e não reivindico o desempenho de nenhum dos compiladores ao compilar o código C ++.

No Ubuntu 15.10, x86.64 e em um processador AMD Phenom (tm) II X6 1090T.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.