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 -O3
possí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?
Respostas:
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
time
comando 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 finalmake check
desta forma: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).
É 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:
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, umstd::shared_ptr<X>
. O viés parastd::unique_ptr
era tolo, já que esses ponteiros eram de fato transferidos, masstd::unique_ptr
parecia a opção mais adequada para substituirstd::auto_ptr
em 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 criarstd::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;Os pontos de observação aqui são:
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:
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:
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
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
-O2
e-O3
margens 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
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-O3
melhoria 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
-O3
otimizaçã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.
fonte
kcachegrind
para identificar as funções em que os executáveis gerados diferem no desempenho.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.
fonte
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 .
fonte
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
-Og
opçã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.fonte
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.
fonte
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:
Então, o gcc, ao compilar com
-O3
ou-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):
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.
fonte
Basicamente falando, a resposta é: depende. Existem muitos benchmarks focados em diferentes tipos de aplicação.
Minha referência no meu aplicativo é: gcc> icc> clang.
Existem E / S raras, mas muitas operações de flutuação da CPU e estrutura de dados.
sinalizadores de compilação é -Wall -g -DNDEBUG -O3.
https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark
fonte