Escrevendo em C para desempenho? [fechadas]

32

Eu sei que ouvi muitas vezes que C normalmente tem uma vantagem de desempenho sobre C ++. Eu realmente não pensei em mais nada até perceber que o MSVC nem parece suportar o mais novo padrão de C, mas o mais novo é compatível com o C99 (tanto quanto eu sei).

Eu estava pensando em escrever uma biblioteca com algum código para renderizar no OpenGL para poder reutilizá-la. Eu estava planejando escrever a biblioteca em C, pois qualquer aumento de desempenho é bem-vindo quando se trata de gráficos.

Mas valeria mesmo a pena? O código que usa a biblioteca provavelmente seria escrito em C ++ e eu prefiro codificar em C ++ em geral.

No entanto, se isso produzisse uma pequena diferença no desempenho, eu provavelmente iria com C.

Também é possível notar que essa biblioteca seria algo que eu faria para funcionar no Windows / OS X / Linux e provavelmente compilaria tudo de forma nativa (MSVC para Windows, Clang ou GCC para OS X e GCC para Linux .. .ou possivelmente compiladores da Intel para tudo).

Eu olhei em volta e encontrei alguns benchmarks e tal, mas tudo o que vi lidou com o GCC, e não com o MSVC e o Clang. Além disso, os benchmarks não mencionam os padrões dos idiomas usados. Alguém tem alguma opinião sobre isso?

EDITAR:Eu só queria compartilhar meu ponto de vista sobre essa questão depois de mais alguns anos de experiência. Acabei escrevendo o projeto para o qual estava fazendo essa pergunta em C ++. Comecei outro projeto na mesma época em C, quando procurávamos obter uma pequena quantidade de desempenho possível e precisávamos que o projeto fosse vinculado em C. Há alguns meses, cheguei ao ponto em que realmente precisava de mapas e avançava manipulação de cordas. Eu conhecia as habilidades para isso na biblioteca padrão C ++ e, finalmente, cheguei à conclusão de que essas estruturas na biblioteca padrão provavelmente teriam um desempenho superior e seriam mais estáveis ​​do que mapas e seqüências de caracteres que eu poderia implementar em C em um período de tempo razoável. O requisito de ser vinculável em C foi facilmente atendido gravando uma interface C no código C ++, que foi feito rapidamente com tipos opacos. Reescrever a biblioteca em C ++ parecia ser muito mais rápido do que quando a escrevia em C e era menos propenso a erros, especialmente vazamentos de memória. Também pude usar a biblioteca de encadeamento de bibliotecas padrão, que foi muito mais fácil do que usar implementações específicas da plataforma. No final, acredito que escrever a biblioteca em C ++ levou a grandes benefícios com possivelmente um pequeno custo de desempenho. Ainda não comparei a versão C ++, mas acredito que seja possível que eu tenha ganho algum desempenho usando estruturas de dados de biblioteca padrão do que as que escrevi. Acredito que escrever a biblioteca em C ++ levou a grandes benefícios com possivelmente um pequeno custo de desempenho. Ainda não comparei a versão C ++, mas acredito que seja possível que eu tenha ganho algum desempenho usando estruturas de dados de biblioteca padrão do que as que escrevi. Acredito que escrever a biblioteca em C ++ levou a grandes benefícios com possivelmente um pequeno custo de desempenho. Ainda não comparei a versão C ++, mas acredito que seja possível que eu tenha ganho algum desempenho usando estruturas de dados de biblioteca padrão do que as que escrevi.

danielunderwood
fonte
9
O mais novo suporte a MSVC é realmente C89.
detly
4
@detly No Visual Studio 2013, a grande maioria dos recursos do C99 é suportada . Não é um suporte completo, mas aposto que na prática é bom usá-lo para escrever C99.
Congusbongus
4
@ danielu13 - Onde você ouviu exatamente que C tem uma vantagem de desempenho sobre C ++?
Ramhound
1
@ Sebastian-LaurenţiuPlesciuc Não acho que esses links sejam realmente úteis. O primeiro poderia ser bem combatido com quase a mesma pergunta programmers.stackexchange.com/q/113295/76444, mas a favor do c ++ em vez de c como no seu link. Para o seu segundo link, é apenas um discurso retórico de linus torvalds. Espero que todos já saibam que ele realmente gosta de odiar c ++ e não o tocaria com muita força, mas seus comentários sobre c ++ são pouco objetivos, cheios de opiniões e preconceitos pessoais e realmente não refletem a realidade da linguagem. . Pelo menos é a minha opinião .
User1942027
1
@RaphaelMiedl Também mencionei que isso foi escrito em 2007, há muito tempo, os compiladores C ++ e a linguagem C ++ evoluíram desde então. Independentemente disso, cabe ao programador escolher qual idioma usar.
Sebastian-Laurenţiu Plesciuc

Respostas:

89

Eu acho que as pessoas costumam afirmar que C é mais rápido que C ++ porque é mais fácil argumentar sobre o desempenho em C. C ++ não é inerentemente mais lento ou mais rápido, mas certos códigos C ++ podem ocultar as penalidades ocultas de desempenho. Por exemplo, pode haver cópias e conversões implícitas que não são visíveis imediatamente quando se olha para algum pedaço de código C ++.

Vamos dar a seguinte declaração:

foo->doSomething(a + 5, *c);

Vamos supor ainda que doSomethingtenha a seguinte assinatura:

void doSomething(int a, long b);

Agora, vamos tentar analisar o possível impacto no desempenho dessa declaração em particular.

Em C, as implicações são bem claras. foopode ser apenas um ponteiro para uma estrutura e doSomethingdeve ser um ponteiro para uma função. *cdesreferencia um longo e a + 5é adição de número inteiro. A única incerteza vem do tipo de a: se não for um int, haverá alguma conversão. além disso, é fácil quantificar o impacto no desempenho dessa única declaração.

Agora vamos mudar para C ++. A mesma declaração agora pode ter características de desempenho muito diferentes:

  1. doSomethingpoderia ser uma função membro não virtual (barata), função membro virtual (um pouco mais cara) std::function, lambda ... etc. O pior é que foopoderia ser uma sobrecarga de tipo de classe operator->com alguma operação de complexidade desconhecida. Portanto, para quantificar o custo da chamada doSomething, agora é necessário conhecer a natureza exata de fooe doSomething.
  2. apode ser um número inteiro ou uma referência a um número inteiro (indireto adicional) ou um tipo de classe que implementa operator+(int). O operador pode até retornar outro tipo de classe que é implicitamente conversível int. Novamente, o custo de desempenho não é aparente apenas na declaração.
  3. cpode ser um tipo de classe implementado operator*(). Também poderia ser uma referência a um long*etc.

Você entendeu a foto. Devido a C ++ 's recursos de linguagem, é muito mais difícil de quantificar custos de desempenho de uma única declaração do que em C. Agora, além disso, abstrações como std::vector, std::stringsão comumente usados em C ++, que têm características de seu próprio desempenho, e as alocações de memória hide dinâmico ( veja também a resposta de @ Ian).

Portanto, o ponto principal é: Em geral, não há diferença no possível desempenho possível usando C ou C ++. Mas, para um código realmente crítico para o desempenho, as pessoas geralmente preferem usar C porque há muito menos penalidades ocultas possíveis no desempenho.

guitarra letal
fonte
1
Resposta soberba. Isso é o que eu estava aludindo na minha resposta, mas você explicou muito melhor.
Ian Goldby
4
Esta deve realmente ser a resposta aceita. Ele explica por que existem declarações como "C é mais rápido que C ++". C pode ser mais rápido ou mais lento que C ++, mas geralmente é muito mais fácil descobrir por que um pedaço específico de código C é rápido / lento, o que geralmente também facilita a otimização.
Leo
Não tire nada dessa excelente resposta (da qual um +1 é meu), mas o (s) compilador (es) podem ser as maçãs e as laranjas nesta comparação. Eles podem gerar código idêntico para C vs. C ++, ou não. É claro que o mesmo pode ser dito para quaisquer dois compiladores ou opções de compilador, mesmo quando compilar fisicamente o mesmo programa com as mesmas premissas do idioma de origem.
JRobert
4
Eu acrescentaria que a maioria dos tempos de execução do C ++ é massiva em comparação com o tempo de execução C equivalente, o que importaria se você estivesse com restrição de memória.
James Anderson
@JamesAnderson Se você for realmente que a memória restrita, você provavelmente não precisa de um tempo de execução em todos os :)
Navin
30

O código escrito em C ++ pode ser mais rápido que em C, para certos tipos de tarefas.

Se você preferir C ++, use C ++. Quaisquer problemas de desempenho serão insignificantes em comparação com as decisões algorítmicas do seu software.

whatsisname
fonte
6
Pode ser mais rápido, mas pode não ser mais rápido pelo mesmo motivo.
7774 Rob
Você pode dar alguns exemplos de código otimizado escrito em C ++ mais rápido que o C otimizado?
1
@ TomDworzanski: um exemplo é que, usando modelos, as decisões sobre os caminhos de código podem ser determinadas em tempo de compilação e acabam codificadas no binário final, em vez de condicionais e ramificações, conforme seria necessário se fosse escrito em c, bem como a capacidade para evitar chamadas de função através de inlining.
Whatsisname
23

Um dos princípios de design do C ++ é que você não paga por recursos que não usa. Portanto, se você escrever código em C ++ e evitar recursos que não existem em C, o código compilado resultante deverá ser equivalente em desempenho (embora você precise medir isso).

Há um custo insignificante para o uso de classes, por exemplo, em comparação com estruturas e várias funções associadas. As funções virtuais custam um pouco mais e você precisa medir o desempenho para ver se isso é importante para o seu aplicativo. O mesmo vale para qualquer outro recurso da linguagem C ++.

Greg Hewgill
fonte
3
A sobrecarga de despacho da função virtual é praticamente insignificante, a menos que você tenha exagerado na decomposição e na virtualização das coisas. As vtables serão pequenas em comparação com o restante do seu código e dados, e a ramificação indexada na vtable adiciona alguns relógios a cada chamada de rotina. Dado que as invocações de rotina, todas chamadas telefônicas para retornar, estarão entre algumas centenas e alguns milhões de relógios, o ramo da tabela será enterrado no chão do barulho.
John R. Strohm
6
Estruturas são classes em C ++.
rightfold
2
@ rightfold: Claro, mas você ainda pode escrever código C ++ que passa ponteiros para estruturas sem usar o thisrecurso de linguagem de ponteiros. Era tudo o que eu estava dizendo.
Greg Hewgill
4
@ John O custo real não é o indireto (embora eu tenha certeza de que isso também estraga um pouco a pré-busca de alguns processadores), mas o fato de que você não pode integrar funções virtuais (pelo menos em C ++), o que não permite muitas outras possíveis otimizações. E sim, isso pode ter uma influência gigantesca no desempenho.
Voo
2
Para ser justo, o mesmo pode ser dito sobre o código C equivalente (código que emula manualmente o polimorfismo em tempo de execução). A maior diferença é que acredito que seria mais fácil para um compilador determinar se essa função poderia ser incorporada em C ++.
Thomas Eding
14

Uma razão pela qual as linguagens de nível superior são às vezes mais lentas é que elas podem esconder nos bastidores muito mais gerenciamento de memória do que as linguagens de nível inferior.

Qualquer idioma (ou biblioteca, API etc.) que abstraia detalhes de baixo nível pode estar ocultando operações caras. Por exemplo, em alguns idiomas, simplesmente aparar o espaço em branco à direita de uma sequência resulta em uma alocação de memória e uma cópia da sequência. A alocação e a cópia de memória, em particular, podem ficar caras se ocorrerem repetidamente em um circuito fechado.

Se você escrevesse esse tipo de código em C, seria óbvio. Em C ++, possivelmente menos, porque as alocações e cópias podem ser abstraídas para uma classe em algum lugar. Eles podem até estar escondidos atrás de um operador sobrecarregado de aparência inocente ou de um construtor de cópias.

Portanto, use C ++, se quiser. Mas não se deixe enganar pela aparente conveniência das abstrações quando não souber o que está por baixo delas.

Obviamente, use um criador de perfil para descobrir o que realmente está deixando seu código lento.

Ian Goldby
fonte
5

Pelo que vale, costumo escrever minhas bibliotecas em C ++ 11 para o conjunto de recursos aprimorados. Eu gosto de poder tirar proveito de coisas como ponteiros compartilhados, exceções, programação genérica e outros recursos exclusivos do C ++. Eu gosto do C ++ 11 porque descobri que boa parte dele é suportada em todas as plataformas de que me preocupo. O Visual Studio 2013 tem muitos dos principais recursos da linguagem e implementações de bibliotecas prontos e supostamente está trabalhando para adicionar o restante. Como você bem sabe, Clang e GCC também suportam todo o conjunto de recursos.

Com isso dito, li recentemente sobre uma ótima estratégia em relação ao desenvolvimento de bibliotecas que acho diretamente relevante para sua consulta. O artigo é intitulado "Estilo de tratamento de erros de CA que funciona bem com exceções em C ++" Stefanu Du Toit se refere a essa estratégia como um padrão de "ampulheta". O primeiro parágrafo do artigo:

Escrevi muito código de biblioteca usando o que chamo de padrão de "ampulheta": implemento uma biblioteca (no meu caso, normalmente usando C ++), envolvo-a em uma API C que se torna o único ponto de entrada da biblioteca, em seguida, agrupe essa API C em C ++ ou em algum outro idioma para fornecer uma abstração rica e sintaxe conveniente. Quando se trata de código nativo de plataforma cruzada, as APIs C fornecem estabilidade incomparável da ABI e portabilidade para outros idiomas por meio de FFIs. Eu até restrico a API a um subconjunto de C que eu sei que é portátil para uma ampla variedade de FFIs e isola a biblioteca de vazamentos de alterações nas estruturas de dados internas - espero mais disso em futuras postagens do blog.


Agora, para resolver sua principal preocupação: desempenho.

Eu sugeriria, como muitas das outras respostas aqui, que escrever código em qualquer idioma funcionaria igualmente do ponto de vista de desempenho. Do ponto de vista pessoal, acho mais fácil escrever código correto em C ++ devido aos recursos da linguagem, mas acho que é uma preferência pessoal. De qualquer forma, os compiladores são realmente inteligentes e tendem a escrever um código melhor do que você. Isso significa que o compilador provavelmente otimizará seu código melhor do que você poderia.

Sei que muitos programadores dizem isso, mas a primeira coisa que você deve fazer é escrever seu código, criar um perfil e fazer otimizações onde seu criador de perfil sugere que você faça. Seu tempo será muito melhor gasto produzindo recursos e otimizando-o assim que você puder ver onde estão seus gargalos.


Agora, para algumas leituras divertidas sobre como os recursos e otimizações de idiomas podem realmente funcionar a seu favor:

std :: unique_ptr tem zero de sobrecarga

constexp permite a computação em tempo de compilação

mover semântica evita objetos temporários desnecessários

vmrob
fonte
std::unique_ptr has zero overheadIsso não pode ser verdade (tecnicamente falando) porque ele deve ter seu construtor chamado se a pilha se desenrolar devido a uma exceção. Um ponteiro bruto não possui essa sobrecarga e ainda estará correto se o seu código provavelmente não for lançado. Um compilador não poderá provar isso no caso geral.
Thomas Eding
2
@ThomasEding Eu estava me referindo ao tamanho e à sobrecarga do tempo de execução em relação ao código sem exceção. Corrija-me se eu estiver errado, mas existem modelos de execução que incorrem em sobrecarga de tempo de execução zero quando não são lançadas exceções que ainda permitem a propagação de exceções quando necessário. Mesmo assim, quando uma exceção poderia ser lançada no construtor de unique_ptr? Ele é declarado noexcepte, no mínimo, lida com todas as exceções, mas não consigo imaginar que tipo de exceção poderia ser lançado.
Vmrob
vmrob: Perdoe-me ... eu pretendia escrever "destruidor" em vez de "construtor". Eu também pretendia escrever "provavelmente não jogará" também. Eeek!
Thomas Eding
2
@ Thomashoding Sabe, eu não acho que importaria se o destruidor lançasse uma exceção. Contanto que o destruidor não introduza novas exceções, ainda será zero a sobrecarga. Além disso, acredito que o destruidor inteiro é incorporado a uma única chamada de exclusão / livre com otimizações.
Vmrob
4

A diferença de desempenho entre C ++ e C não se deve a nada na linguagem, estritamente falando, mas no que ela tenta fazer. É como um cartão de crédito versus dinheiro. Isso não faz você gastar mais, mas você gasta de qualquer maneira, a menos que seja muito disciplinado.

Aqui está um exemplo de um programa escrito em C ++, que foi então ajustado agressivamente ao desempenho. Você precisa saber como fazer ajustes de desempenho agressivos, independentemente do idioma. O método que eu uso é uma pausa aleatória, como mostrado neste vídeo .

Os tipos de coisas caras que o C ++ tenta fazer são gerenciamento excessivo de memória, programação no estilo de notificação, confiando no contador do programa em bibliotecas de abstração de várias camadas (como @Ian disse), ocultação de lentidão etc.

Mike Dunlavey
fonte
2

C não tem nenhuma vantagem de desempenho em comparação com C ++ se você fizer as mesmas coisas nos dois idiomas. Você pode pegar qualquer código C antigo criado por qualquer programador C decente e transformá-lo em código C ++ válido e equivalente, que será executado com a mesma rapidez (a menos que você e seu compilador saibam o que a palavra-chave "restringir" faz e a usem com eficiência, mas a maioria das pessoas não).

O C ++ pode ter um desempenho extremamente diferente, mais lento ou mais rápido, se (1) você usar a biblioteca C ++ padrão para fazer coisas que podem ser feitas muito mais rapidamente e com mais facilidade sem usar a biblioteca, ou (2) se você usar a biblioteca C ++ padrão para fazer as coisas com muito mais facilidade e rapidez do que reimplementar a biblioteca em C. ruim

gnasher729
fonte
1
isso não parece oferecer nada substancial sobre o que foi explicado em 6 respostas anteriores #
306
Penso que esta resposta menciona um ponto importante que ninguém mais mencionou. À primeira vista, parece que C ++ é um superconjunto de C; portanto, se você puder escrever uma implementação rápida de C, deverá poder escrever uma implementação de C ++ equivalente. No entanto, o C99 suporta a palavra-chave restrita, que permite evitar aliases indesejados do ponteiro. C ++ não tem esse suporte. A capacidade de evitar o alias do ponteiro é um recurso importante do Fortran que o torna útil para aplicativos de alto desempenho. Espero que também seja possível obter melhor desempenho do C99 do que o C ++ em domínios semelhantes.
user27539