Desempenho C ++ vs. Java / C #

Meu entendimento é que o C / C ++ produz código nativo para ser executado em uma arquitetura de máquina específica. Por outro lado, linguagens como Java e C # são executadas em cima de uma máquina virtual que abstrai a arquitetura nativa. Logicamente, parece impossível para o Java ou o C # corresponder à velocidade do C ++ devido a essa etapa intermediária, mas me disseram que os compiladores mais recentes (“hot spot”) podem atingir essa velocidade ou até excedê-la.

Talvez isso seja mais uma questão de compilador do que uma questão de linguagem, mas alguém pode explicar em inglês como é possível que uma dessas linguagens de máquina virtual funcione melhor do que uma linguagem nativa?

Geralmente, C # e Java podem ser tão rápidos ou mais rápidos porque o compilador JIT – um compilador que compila sua IL na primeira vez que é executado – pode fazer otimizações que um programa compilado C ++ não pode porque pode consultar a máquina. Pode determinar se a máquina é Intel ou AMD; Pentium 4, Core Solo ou Core Duo; ou se suporta SSE4, etc.

Um programa C ++ tem que ser compilado anteriormente com otimizações mistas para que ele funcione decentemente bem em todas as máquinas, mas não é otimizado tanto quanto poderia ser para uma configuração única (ou seja, processador, conjunto de instruções, outro hardware).

Além disso, determinados resources de linguagem permitem que o compilador em C # e Java faça suposições sobre seu código, o que permite otimizar certas partes que não são seguras para o compilador C / C ++. Quando você tem access a pointers, há muitas otimizações que não são seguras.

Além disso, Java e C # podem fazer alocações de heap com mais eficiência do que C ++, porque a camada de abstração entre o coletor de lixo e seu código permite que ele faça toda a compactação de heap de uma só vez (uma operação bastante cara).

Agora não posso falar em Java neste próximo ponto, mas sei que o C #, por exemplo, removerá methods e chamadas de método quando souber que o corpo do método está vazio. E usará esse tipo de lógica em todo o seu código.

Então, como você pode ver, existem várias razões pelas quais determinadas implementações C # ou Java serão mais rápidas.

Agora, tudo isso dito, otimizações específicas podem ser feitas em C ++ que irão acabar com qualquer coisa que você poderia fazer com C #, especialmente no mundo gráfico e sempre que estiver perto do hardware. Ponteiros fazem maravilhas aqui.

Então, dependendo do que você está escrevendo eu iria com um ou outro. Mas se você está escrevendo algo que não é dependente de hardware (driver, videogame, etc), eu não me preocuparia com o desempenho do C # (novamente não posso falar sobre Java). Vai dar tudo certo.

Um lado do Java, o @Swati aponta um bom artigo:

https://www.ibm.com/developerworks/library/j-jtp09275

JIT vs. Compilador Estático

Como já foi dito nos posts anteriores, o JIT pode compilar IL / bytecode para código nativo em tempo de execução. O custo disso foi mencionado, mas não para sua conclusão:

O JIT tem um grande problema é que ele não pode compilar tudo: a compilation JIT leva tempo, então o JIT compilará apenas algumas partes do código, enquanto um compilador estático produzirá um binário nativo completo: Para algum tipo de programa, o estático o compilador simplesmente superará facilmente o JIT.

Obviamente, C # (ou Java, ou VB) é geralmente mais rápido para produzir uma solução viável e robusta do que C ++ (apenas porque C ++ tem semântica complexa, e a biblioteca padrão C ++, embora interessante e poderosa, é bastante pobre quando comparada com a completa escopo da biblioteca padrão de .NET ou Java), então, normalmente, a diferença entre C ++ e .NET ou Java JIT não será visível para a maioria dos usuários, e para aqueles binários que são críticos, bem, você ainda pode chamar o processamento de C ++ a partir de C # ou Java (mesmo que esse tipo de chamadas nativas possa ser caro em si) …

Metaprogramação de C ++

Observe que, geralmente, você está comparando o código de tempo de execução C ++ com seu equivalente em C # ou Java. Mas o C ++ tem um recurso que pode superar o Java / C #, ou seja, metaprogramação do modelo: O processamento do código será feito no tempo de compilation (aumentando assim o tempo de compilation) resultando em zero (ou quase zero) tempo de execução.

Eu ainda vejo um efeito real sobre isso (eu joguei apenas com conceitos, mas a diferença era segundos de execução para JIT, e zero para C ++), mas isso vale a pena mencionar, ao lado da metaprogramação do modelo de fato não é trivial…

Editar 2011-06-10: Em C ++, jogando com os tipos é feito em tempo de compilation, ou seja, produzindo código genérico que chama código não genérico (por exemplo, um analisador genérico de string para digitar T, chamando API de biblioteca padrão para tipos T reconhece, e tornando o analisador facilmente extensível pelo seu usuário) é muito fácil e muito eficiente, enquanto o equivalente em Java ou C # é doloroso para escrever, e sempre será mais lento e resolvido em tempo de execução mesmo quando os tipos são conhecidos em tempo de compilation, ou seja, sua única esperança é que o JIT insira a coisa toda.

Edit 2011-09-20: A equipe por trás do Blitz ++ ( Homepage , Wikipedia ) foi dessa forma e, aparentemente, seu objective é atingir o desempenho do FORTRAN em cálculos científicos movendo o máximo possível da execução em tempo de execução para o tempo de compilation, via metaprogramação de modelo C ++ . Então, o “ eu ainda vejo um efeito da vida real nessa parte” que escrevi acima aparentemente existe na vida real.

Uso de memory C ++ nativa

O C ++ possui um uso de memory diferente do Java / C # e, portanto, possui diferentes vantagens / falhas.

Não importa a otimização do JIT, nada irá tão rápido quanto o access direto do ponteiro à memory (vamos ignorar por um momento o cache do processador, etc.). Então, se você tem dados contíguos na memory, acessá-los através de pointers C ++ (isto é, pointers C … Vamos dar o Caesar por vencido) vai mais rápido do que em Java / C #. E o C ++ tem RAII, o que torna muito mais fácil o processamento do que em C # ou mesmo em Java. C ++ não precisa using para escopo a existência de seus objects. E o C ++ não tem uma cláusula finally . Isso não é um erro.

🙂

E apesar das estruturas primitivas do C #, os objects “on the stack” do C ++ não custarão nada na alocação e na destruição, e não precisarão do GC para trabalhar em um thread independente para fazer a limpeza.

Quanto à fragmentação de memory, os alocadores de memory em 2008 não são os antigos alocadores de memory de 1980 que são geralmente comparados com um GC: A alocação de C ++ não pode ser movida na memory, verdade, mas, como em um sistema de arquivos Linux: Quem precisa de disco rígido desfragmentar quando a fragmentação não acontece? Usar o alocador certo para a tarefa correta deve fazer parte do kit de ferramentas do desenvolvedor C ++. Agora, escrever alocadores não é fácil, e então, a maioria de nós tem coisas melhores para fazer, e para o máximo de uso, RAII ou GC é mais do que suficiente.

Edit 2011-10-04: Para exemplos sobre alocadores eficientes: Em plataformas Windows, desde o Vista, o Heap de Fragmentação Baixa é habilitado por padrão. Para versões anteriores, o LFH pode ser ativado chamando a function WineapI HeapSetInformation ). Em outros sistemas operacionais, alocadores alternativos são fornecidos (consulte https://secure.wikimedia.org/wikipedia/en/wiki/Malloc para obter uma lista)

Agora, o modelo de memory está se tornando um pouco mais complicado com o surgimento da tecnologia multicore e multithreading. Nesse campo, acho que o .NET tem a vantagem, e o Java, segundo me disseram, ocupava o primeiro andar. É fácil para alguns hackers “no metal puro” elogiar seu código “próximo da máquina”. Mas agora, é muito mais difícil produzir melhor assembly à mão do que deixar o compilador em seu trabalho. Para C ++, o compilador tornou-se geralmente melhor que o hacker desde uma década. Para C # e Java, isso é ainda mais fácil.

Ainda assim, o novo padrão C ++ 0x irá impor um modelo de memory simples para compiladores C ++, que irá padronizar (e simplificar) o código multiprocessing / paralelo / threading efetivo em C ++, e tornar as otimizações mais fáceis e seguras para compiladores. Mas então, veremos em alguns anos se suas promises são verdadeiras.

C ++ / CLI vs. C # / VB.NET

Nota: Nesta seção, estou falando sobre C ++ / CLI, ou seja, o C ++ hospedado pelo .NET, não o C ++ nativo.

Na semana passada, tive um treinamento sobre otimização .NET e descobri que o compilador estático é muito importante. Tão importante que o JIT.

O mesmo código compilado em C ++ / CLI (ou seu ancestral, Managed C ++) poderia ser mais rápido que o mesmo código produzido em C # (ou VB.NET, cujo compilador produz o mesmo IL que C #).

Porque o compilador estático C ++ foi muito melhor para produzir código já otimizado que o do C #.

Por exemplo, a function inlining no .NET é limitada a funções cujo bytecode é menor ou igual a 32 bytes de comprimento. Portanto, algum código em C # produzirá um acessador de 40 bytes, que nunca será embutido pelo JIT. O mesmo código em C ++ / CLI produzirá um acessador de 20 bytes, que será embutido pelo JIT.

Outro exemplo são variables ​​temporárias, que são simplesmente compiladas pelo compilador C ++ enquanto ainda são mencionadas no IL produzido pelo compilador C #. A otimização da compilation estática de C ++ resultará em menos código, assim autorizando uma otimização de JIT mais agressiva, novamente.

A razão para isso foi especulado como sendo o fato de o compilador C ++ / CLI ter se beneficiado das vastas técnicas de otimização do compilador nativo C ++.

Conclusão

Eu amo o C ++.

Mas até onde eu vejo, C # ou Java são todos uma aposta melhor. Não porque eles são mais rápidos que o C ++, mas porque quando você sum suas qualidades, eles acabam sendo mais produtivos, precisando de menos treinamento e tendo bibliotecas padrão mais completas que o C ++. E quanto à maioria dos programas, suas diferenças de velocidade (de uma forma ou de outra) serão insignificantes …

Editar (2011-06-06)

Minha experiência no C # / .net

Eu tenho agora 5 meses de codificação C # profissional quase exclusiva (o que se sum ao meu currículo já cheio de C ++ e Java, e um toque de C ++ / CLI).

Eu joguei com WinForms (Ahem …) e WCF (legal!) E WPF (Cool !!!! Tanto através de XAML e C # bruto. WPF é tão fácil, eu acredito que Swing simplesmente não pode se comparar a ele), e C # 4.0.

A conclusão é que, embora seja mais fácil / rápido produzir um código que funcione em C # / Java do que em C ++, é muito mais difícil produzir um código forte, seguro e robusto em C # (e ainda mais difícil em Java) do que em C ++. As razões são abundantes, mas podem ser resumidas por:

  1. Os genéricos não são tão poderosos quanto os modelos ( tente escrever um método Parse genérico eficiente (de string para T), ou um equivalente eficiente de boost :: lexical_cast em C # para entender o problema )
  2. O RAII permanece incomparável (o GC ainda pode vazar (sim, eu tive que lidar com esse problema) e só lida com a memory. Mesmo o uso do C # não é tão fácil e poderoso porque é difícil escrever implementações Dispose corretas )
  3. C # readonly e Java final não são tão úteis como as const C ++ ( não há como expor dados totalmente desnecessários (uma tree de nós, por exemplo) em C # sem um tremendo trabalho, embora seja um recurso interno do C ++. uma solução interessante, mas nem tudo pode ser imutável, então não é suficiente, de longe ).

Então, o C # continua sendo uma linguagem agradável, desde que você queira algo que funcione, mas uma linguagem frustrante no momento em que você quer algo que sempre e com segurança funciona.

Java é ainda mais frustrante, pois tem os mesmos problemas que C #, e mais: Sem o equivalente de C # using palavra-chave, um colega muito habilidoso gastou muito tempo certificando-se de que seus resources fossem corretamente liberados, enquanto o equivalente em C ++ tem sido fácil (usando destruidores e pointers inteligentes).

Então eu acho que o ganho de produtividade do C # / Java é visível para a maioria dos códigos … até o dia que você precisa que o código seja o mais perfeito possível. Naquele dia, você saberá dor. (você não vai acreditar no que é pedido nos nossos aplicativos de servidor e GUI …).

Sobre o lado do servidor Java e C ++

Mantive contato com as equipes de servidores (trabalhei 2 anos entre eles, antes de voltar para a equipe GUI), do outro lado do prédio, e aprendi algo interessante.

Nos últimos anos, a tendência era que os aplicativos de servidor Java fossem destinados a replace os antigos aplicativos de servidor C ++, já que o Java tem muitos frameworks / ferramentas e é fácil de manter, implantar, etc. etc.

… Até que o problema de baixa latência levantou sua cabeça feia nos últimos meses. Em seguida, os aplicativos de servidor Java, independentemente da otimização tentada por nossa equipe Java especializada, simplesmente perderam a corrida contra o servidor C ++ antigo, não muito otimizado.

Atualmente, a decisão é manter os servidores Java para uso comum onde o desempenho ainda é importante, não está preocupado com o objective de baixa latência e otimiza agressivamente os aplicativos de servidor C ++ já mais rápidos para necessidades de baixa latência e latência ultrabaixa.

Conclusão

Nada é tão simples quanto o esperado.

Java, e ainda mais C #, são linguagens legais, com extensas bibliotecas e frameworks padrão, onde você pode codificar rapidamente e ter resultados muito em breve.

Mas quando você precisa de força bruta, otimizações poderosas e sistemáticas, suporte forte ao compilador, resources poderosos de linguagem e segurança absoluta, Java e C # tornam difícil vencer as últimas porcentagens de qualidade faltantes, mas críticas, que você precisa manter acima da concorrência.

É como se você precisasse de menos tempo e desenvolvedores menos experientes em C # / Java do que em C ++ para produzir código de qualidade média, mas, por outro lado, no momento em que você precisava de código de qualidade excelente para perfeito, era mais fácil e rápido obter os resultados. em C ++.

Claro, esta é a minha própria percepção, talvez limitada às nossas necessidades específicas.

Mas ainda assim, é o que acontece hoje, tanto nas equipes de GUI quanto nas equipes do lado do servidor.

Claro, atualizarei este post se algo novo acontecer.

Editar (2011-06-22)

“Descobrimos que, no que diz respeito ao desempenho, o C ++ vence por uma grande margem. No entanto, também exigiu os esforços de ajuste mais extensos, muitos dos quais foram feitos em um nível de sofisticação que não estaria disponível para o programador médio.

[…] A versão Java foi provavelmente a mais simples de implementar, mas a mais difícil de analisar quanto ao desempenho. Especificamente, os efeitos em torno da garbage collection foram complicados e muito difíceis de ajustar. ”

Fontes:

Editar (2011-09-20)

“A palavra no Facebook é que ‘ código C ++ razoavelmente escrito é executado rapidamente, o que ressalta o enorme esforço gasto na otimização do código PHP e Java. Paradoxalmente, o código C ++ é mais difícil de escrever do que em outras linguagens, mas o código eficiente é um muito mais fácil [escrever em C ++ do que em outros idiomas].

– Herb Sutter em // build / , citando Andrei Alexandrescu

Fontes:

Sempre que falo em desempenho gerenciado versus não gerenciado, gosto de apontar para a série que Rico (e Raymond) fizeram comparando as versões C ++ e C # de um dictionary chinês / inglês. Esta pesquisa no google vai deixar você ler por si mesmo, mas eu gosto do resumo de Rico.

Então eu estou envergonhado pela minha derrota esmagadora? Dificilmente. O código gerenciado obteve um resultado muito bom por quase nenhum esforço. Para derrotar o gerenciado Raymond teve que:

  • Escreva seu próprio material de E / S de arquivo
  • Escreva sua própria class de string
  • Escreva seu próprio alocador
  • Escreva seu próprio mapeamento internacional

É claro que ele usou bibliotecas de nível inferior disponíveis para fazer isso, mas ainda é muito trabalho. Você pode chamar o que sobrou de um programa STL? Eu não penso assim, eu acho que ele manteve a class std :: vector que no final nunca foi um problema e ele manteve a function find. Quase tudo o resto se foi.

Então, sim, você pode definitivamente bater o CLR. Raymond pode fazer seu programa ir ainda mais rápido, eu acho.

Curiosamente, o tempo para analisar o arquivo, conforme relatado por ambos os programas de timeres internos é aproximadamente o mesmo – 30ms para cada um. A diferença está na sobrecarga.

Para mim, a questão é que foram necessárias 6 revisões para a versão não gerenciada para superar a versão gerenciada que era uma porta simples do código não gerenciado original. Se você precisar de todo o desempenho (e tiver tempo e experiência para obtê-lo), você terá que ficar sem gerenciamento, mas, para mim, vou aproveitar a ordem de grandeza que tenho nas primeiras versões ao longo dos 33 % Eu ganho se eu tentar 6 vezes.

A compilation para otimizações de CPU específicas geralmente é superestimada. Basta pegar um programa em C ++ e compilar com otimização para pentium PRO e rodar em um pentium 4. Então recompile com otimizar para pentium 4. Passei longas tardes fazendo isso com vários programas. Resultados gerais ?? Geralmente menos de 2-3% de aumento de desempenho. Portanto, as vantagens teóricas do JIT são quase nenhuma. A maioria das diferenças de desempenho só pode ser observada ao usar resources de processamento de dados escalares, algo que eventualmente precisará de ajuste fino manual para atingir o máximo desempenho de qualquer maneira. Otimizações desse tipo são lentas e caras para executar, tornando-as, às vezes, inadequadas para o JIT.

No mundo real e na aplicação real, o C ++ ainda é geralmente mais rápido do que o java, principalmente por causa da pegada de memory mais leve que resulta em melhor desempenho do cache.

Mas para usar todo o recurso de C ++, o desenvolvedor deve trabalhar duro. Você pode alcançar resultados superiores, mas você deve usar seu cérebro para isso. C ++ é uma linguagem que decidiu apresentar a você mais ferramentas, cobrando o preço que você deve aprendê-las para poder usar bem a linguagem.

O JIT (Just In Time Compiling) pode ser incrivelmente rápido porque otimiza para a plataforma de destino.

Isso significa que ele pode tirar proveito de qualquer truque de compilador que sua CPU possa suportar, independentemente da CPU na qual o desenvolvedor escreveu o código.

O conceito básico do .NET JIT funciona assim (muito simplificado):

Chamando um método pela primeira vez:

  • Seu código de programa chama um método Foo ()
  • O CLR examina o tipo que implementa Foo () e obtém os metadados associados a ele
  • A partir dos metadados, o CLR sabe em qual endereço de memory o IL (código de byte intermediário) está armazenado.
  • O CLR aloca um bloco de memory e chama o JIT.
  • O JIT compila o IL em código nativo, coloca-o na memory alocada e, em seguida, altera o ponteiro de function em metadados do tipo Foo () para apontar para esse código nativo.
  • O código nativo é executado.

Chamando um método pela segunda vez:

  • Seu código de programa chama um método Foo ()
  • O CLR examina o tipo que implementa Foo () e localiza o ponteiro de function nos metadados.
  • O código nativo neste local de memory é executado.

Como você pode ver, a segunda vez, é praticamente o mesmo processo que o C ++, exceto com a vantagem das otimizações em tempo real.

Dito isto, há ainda outros problemas gerais que retardam uma linguagem gerenciada, mas o JIT ajuda muito.

Eu gosto da resposta de Orion Adrian , mas há outro aspecto nisso.

A mesma pergunta foi feita há décadas sobre linguagem assembly versus linguagens “humanas” como FORTRAN. E parte da resposta é semelhante.

Sim, um programa C ++ é capaz de ser mais rápido que C # em qualquer algoritmo (não-trivial?), Mas o programa em C # geralmente será mais rápido ou mais rápido do que uma implementação “ingênua” em C ++ e uma versão otimizada em C ++ levará mais tempo para se desenvolver e ainda poderá superar a versão C # por uma margem muito pequena. Então, vale mesmo a pena?

Você terá que responder a essa pergunta em uma base por um.

Dito isso, sou fã de longa data do C ++, e acho que é uma linguagem incrivelmente expressiva e poderosa – às vezes subestimada. Mas em muitos problemas da “vida real” (para mim, pessoalmente, isso significa “o tipo que eu pago para resolver”), o C # fará o trabalho mais cedo e mais seguro.

A maior penalidade que você paga? Muitos programas .NET e Java são invasores de memory. Eu vi aplicativos .NET e Java tomarem “centenas” de megabytes de memory, quando programas C ++ de complexidade semelhante apenas arranham as “dezenas” de MBs.

Não sei com que frequência você encontrará que o código Java será executado mais rápido que o C ++, mesmo com o Hotspot, mas vou explicar como isso pode acontecer.

Pense no código Java compilado como linguagem de máquina interpretada para a JVM. Quando o processador Hotspot percebe que certas partes do código compilado serão usadas muitas vezes, ele realiza uma otimização no código da máquina. Como o ajuste manual do Assembly é quase sempre mais rápido do que o código compilado do C ++, não há problema em descobrir que o código de máquina programaticamente ajustado não será tão ruim.

Assim, para um código altamente repetitivo, pude ver onde seria possível que a JVM do Hotspot executasse o Java mais rápido que o C ++ … até que a garbage collection entrasse em ação. 🙂

Geralmente, o algoritmo do seu programa será muito mais importante para a velocidade do seu aplicativo do que o idioma . Você pode implementar um algoritmo pobre em qualquer idioma, incluindo C ++. Com isso em mente, você geralmente será capaz de escrever código de execução mais rápido em uma linguagem que ajuda a implementar um algoritmo mais eficiente.

Linguagens de nível superior fazem muito bem isso, fornecendo access mais fácil a muitas estruturas de dados pré-construídas eficientes e incentivando práticas que ajudarão a evitar códigos ineficientes. É claro que eles também podem tornar mais fácil escrever um monte de código muito lento, então você ainda precisa conhecer sua plataforma.

Além disso, o C ++ está se atualizando com resources “novos” (observe as aspas) como os contêineres STL, os pointers automáticos, etc – veja a biblioteca de reforço, por exemplo. E você pode ocasionalmente achar que o caminho mais rápido para realizar alguma tarefa requer uma técnica como aritmética de pointers que é proibida em uma linguagem de nível mais alto – embora eles tipicamente permitam que você chame uma biblioteca escrita em uma linguagem que possa implementá-la como desejado .

O principal é saber a linguagem que você está usando, a API associada, o que ela pode fazer e quais são suas limitações.

Eu também não sei … meus programas Java estão sempre lentos. 🙂 Eu realmente nunca notei programas C # sendo particularmente lentos, no entanto.

Aqui está outro benchmark intersting, que você pode experimentar no seu próprio computador.

Compara ASM, VC ++, C #, Silverlight, applet Java, Javascript, Flash (AS3)

Demonstração de velocidade do plugin Roozz

Por favor, note que a velocidade do javascript varia muito dependendo do navegador que está sendo executado. O mesmo vale para o Flash e o Silverlight, porque esses plug-ins são executados no mesmo processo que o navegador de hospedagem. Mas o plugin Roozz executa arquivos .exe padrão, que são executados em seu próprio processo, portanto a velocidade não é influenciada pelo navegador de hospedagem.

Você deve definir “executar melhor que ..”. Bem, eu sei, você perguntou sobre velocidade, mas não é tudo que conta.

  • As máquinas virtuais executam mais sobrecarga de tempo de execução? Sim!
  • Eles comem mais memory de trabalho? Sim!
  • Eles têm custos de boot mais altos (boot de tempo de execução e compilador JIT)? Sim!
  • Eles exigem uma enorme biblioteca instalada? Sim!

E assim por diante, é tendencioso, sim;)

Com C # e Java, você paga um preço pelo que obtém (codificação mais rápida, gerenciamento automático de memory, biblioteca grande e assim por diante). Mas você não tem muito espaço para pechinchar sobre os detalhes: pegue o pacote completo ou nada.

Mesmo que essas linguagens possam otimizar algum código para executar mais rapidamente do que o código compilado, toda a abordagem é (IMHO) ineficiente. Imagine dirigir todos os dias 5 milhas para o seu local de trabalho, com um caminhão! É confortável, é bom, você está seguro (zona de deformação extrema) e depois de pisar no acelerador por algum tempo, será até mesmo tão rápido quanto um carro padrão! Por que todos nós não temos um caminhão para dirigir para o trabalho? 😉

Em C ++ você obtém o que paga, não mais, não menos.

Citando Bjarne Stroustrup: “C ++ é minha linguagem de lixo coletada favorita porque gera muito pouco lixo” link text

O código executável produzido a partir de um compilador Java ou C # não é interpretado – ele é compilado para o código nativo “just in time” (JIT). Assim, o primeiro código de tempo em um programa Java / C # é encontrado durante a execução, há alguma sobrecarga como o “compilador de tempo de execução” (também conhecido como compilador JIT) transforma o código de byte (Java) ou código IL (C #) em instruções nativas da máquina. No entanto, na próxima vez em que o código for encontrado enquanto o aplicativo ainda estiver em execução, o código nativo será executado imediatamente. Isso explica como alguns programas Java / C # parecem inicialmente lentos, mas funcionam melhor quanto mais tempo eles executam. Um bom exemplo é um site da ASP.Net. A primeira vez que o site é acessado, pode ser um pouco mais lento, pois o código C # é compilado em código nativo pelo compilador JIT. Acessos subseqüentes resultam em um site muito mais rápido – o servidor e o cache do lado do cliente estão de lado.

Algumas boas respostas aqui sobre a pergunta específica que você fez. Eu gostaria de dar um passo atrás e olhar para a foto maior.

Lembre-se de que a percepção que o usuário tem da velocidade do software que você escreve é ​​afetada por muitos outros fatores, além de quão bem o codegen é otimizado. aqui estão alguns exemplos:

  • memory management manual é difícil de fazer corretamente (sem vazamentos), e ainda mais difícil de fazer de forma eficiente (memory livre logo depois que você terminar com isso). Usar um GC é, em geral, mais propenso a produzir um programa que gerencie bem a memory. Você está disposto a trabalhar muito e atrasar a entrega de seu software, na tentativa de superar o GC?

  • Meu C # é mais fácil de ler e entender do que o meu C ++. Eu também tenho mais maneiras de me convencer de que meu código c # está funcionando corretamente. Isso significa que eu posso otimizar meus algoritmos com menos risco de introduzir bugs (e os usuários não gostam de falhas de software, mesmo que isso ocorra rapidamente!)

  • Eu posso criar meu software mais rápido em C # do que em C ++. Isso libera tempo para trabalhar no desempenho e ainda entrega meu software no prazo.

  • É mais fácil escrever uma boa interface do usuário em C # do que em C ++, portanto, é mais provável que eu seja capaz de empurrar o trabalho para segundo plano enquanto a IU permanece responsiva ou fornecer progresso ou ouvir a interface do usuário quando o programa precisa ser bloqueado por algum tempo. Isso não torna nada mais rápido, mas deixa os usuários mais felizes em esperar.

Tudo o que eu disse sobre C # é provavelmente verdade para Java, eu simplesmente não tenho a experiência de dizer com certeza.

Se você for um programador Java / C # aprendendo C ++, ficará tentado a continuar pensando em termos de Java / C # e a traduzir textualmente para a syntax C ++. Nesse caso, você só recebe os benefícios mencionados anteriormente do código nativo vs. interpretado / JIT. To get the biggest performance gain in C++ vs. Java/C#, you have to learn to think in C++ and design code specifically to exploit the strengths of C++.

To paraphrase Edsger Dijkstra : [your first language] mutilates the mind beyond recovery.
To paraphrase Jeff Atwood : you can write [your first language] in any new language.

One of the most significant JIT optimizations is method inlining. Java can even inline virtual methods if it can guarantee runtime correctness. This kind of optimization usually cannot be performed by standard static compilers because it needs whole-program analysis, which is hard because of separate compilation (in contrast, JIT has all the program available to it). Method inlining improves other optimizations, giving larger code blocks to optimize.

Standard memory allocation in Java/C# is also faster, and deallocation (GC) is not much slower, but only less deterministic.

The virtual machine languages are unlikely to outperform compiled languages but they can get close enough that it doesn’t matter, for (at least) the following reasons (I’m speaking for Java here since I’ve never done C#).

1/ The Java Runtime Environment is usually able to detect pieces of code that are run frequently and perform just-in-time (JIT) compilation of those sections so that, in future, they run at the full compiled speed.

2/ Vast portions of the Java libraries are compiled so that, when you call a library function, you’re executing compiled code, not interpreted. You can see the code (in C) by downloading the OpenJDK.

3/ Unless you’re doing massive calculations, much of the time your program is running, it’s waiting for input from a very slow (relatively speaking) human.

4/ Since a lot of the validation of Java bytecode is done at the time of loading the class, the normal overhead of runtime checks is greatly reduced.

5/ At the worst case, performance-intensive code can be extracted to a compiled module and called from Java (see JNI) so that it runs at full speed.

In summary, the Java bytecode will never outperform native machine language, but there are ways to mitigate this. The big advantage of Java (as I see it) is the HUGE standard library and the cross-platform nature.

Orion Adrian , let me invert your post to see how unfounded your remarks are, because a lot can be said about C++ as well. And telling that Java/C# compiler optimize away empty functions does really make you sound like you are not my expert in optimization, because a) why should a real program contain empty functions, except for really bad legacy code, b) that is really not black and bleeding edge optimization.

Apart from that phrase, you ranted blatantly about pointers, but don’t objects in Java and C# basically work like C++ pointers? May they not overlap? May they not be null? C (and most C++ implementations) has the restrict keyword, both have value types, C++ has reference-to-value with non-null guarantee. What do Java and C# offer?

>>>>>>>>>>

Generally, C and C++ can be just as fast or faster because the AOT compiler — a compiler that compiles your code before deployment, once and for all, on your high memory many core build server — can make optimizations that a C# compiled program cannot because it has a ton of time to do so. The compiler can determine if the machine is Intel or AMD; Pentium 4, Core Solo, or Core Duo; or if supports SSE4, etc, and if your compiler does not support runtime dispatch, you can solve for that yourself by deploying a handful of specialized binaries.

AC# program is commonly compiled upon running it so that it runs decently well on all machines, but is not optimized as much as it could be for a single configuration (ie processor, instruction set, other hardware), and it must spend some time first. Features like loop fission, loop inversion, automatic vectorization, whole program optimization, template expansion, IPO, and many more, are very hard to be solved all and completely in a way that does not annoy the end user.

Additionally certain language features allow the compiler in C++ or C to make assumptions about your code that allows it to optimize certain parts away that just aren’t safe for the Java/C# compiler to do. When you don’t have access to the full type id of generics or a guaranteed program flow there’s a lot of optimizations that just aren’t safe.

Also C++ and C do many stack allocations at once with just one register incrementation, which surely is more efficient than Javas and C# allocations as for the layer of abstraction between the garbage collector and your code.

Now I can’t speak for Java on this next point, but I know that C++ compilers for example will actually remove methods and method calls when it knows the body of the method is empty, it will eliminate common subexpressions, it may try and retry to find optimal register usage, it does not enforce bounds checking, it will autovectorize loops and inner loops and will invert inner to outer, it moves conditionals out of loops, it splits and unsplits loops. It will expand std::vector into native zero overhead arrays as you’d do the C way. It will do inter procedural optimmizations. It will construct return values directly at the caller site. It will fold and propagate expressions. It will reorder data into a cache friendly manner. It will do jump threading. It lets you write compile time ray tracers with zero runtime overhead. It will make very expensive graph based optimizations. It will do strength reduction, were it replaces certain codes with syntactically totally unequal but semantically equivalent code (the old “xor foo, foo” is just the simplest, though outdated optimization of such kind). If you kindly ask it, you may omit IEEE floating point standards and enable even more optimizations like floating point operand re-ordering. After it has massaged and massacred your code, it might repeat the whole process, because often, certain optimizations lay the foundation for even certainer optimizations. It might also just retry with shuffled parameters and see how the other variant scolors in its internal ranking. And it will use this kind of logic throughout your code.

So as you can see, there are lots of reasons why certain C++ or C implementations will be faster.

Now this all said, many optimizations can be made in C++ that will blow away anything that you could do with C#, especially in the number crunching, realtime and close-to-metal realm, but not exclusively there. You don’t even have to touch a single pointer to come a long way.

So depending on what you’re writing I would go with one or the other. But if you’re writing something that isn’t hardware dependent (driver, video game, etc), I wouldn’t worry about the performance of C# (again can’t speak about Java). It’ll do just fine.

< <<<<<<<<<

Generally, certain generalized arguments might sound cool in specific posts, but don’t generally sound certainly credible.

Anyways, to make peace: AOT is great, as is JIT . The only correct answer can be: It depends. And the real smart people know that you can use the best of both worlds anyways.

It would only happen if the Java interpreter is producing machine code that is actually better optimized than the machine code your compiler is generating for the C++ code you are writing, to the point where the C++ code is slower than the Java and the interpretation cost.

However, the odds of that actually happening are pretty low – unless perhaps Java has a very well-written library, and you have your own poorly written C++ library.

Actually, C# does not really run in a virtual machine like Java does. IL is compiled into assembly language, which is entirely native code and runs at the same speed as native code. You can pre-JIT an .NET application which entirely removes the JIT cost and then you are running entirely native code.

The slowdown with .NET will come not because .NET code is slower, but because it does a lot more behind the scenes to do things like garbage collect, check references, store complete stack frames, etc. This can be quite powerful and helpful when building applications, but also comes at a cost. Note that you could do all these things in a C++ program as well (much of the core .NET functionality is actually .NET code which you can view in ROTOR). However, if you hand wrote the same functionality you would probably end up with a much slower program since the .NET runtime has been optimized and finely tuned.

That said, one of the strengths of managed code is that it can be fully verifiable, ie. you can verify that the code will never access another processes’s memory or do unsage things before you execute it. Microsoft has a research prototype of a fully managed operating system that has suprisingly shown that a 100% managed environment can actually perform significantly faster than any modern operating system by taking advantage of this verification to turn off security features that are no longer needed by managed programs (we are talking like 10x in some cases). SE radio has a great episode talking about this project.

In some cases, managed code can actually be faster than native code. For instance, “mark-and-sweep” garbage collection algorithms allow environments like the JRE or CLR to free large numbers of short-lived (usually) objects in a single pass, where most C/C++ heap objects are freed one-at-a-time.

From wikipedia :

For many practical purposes, allocation/deallocation-intensive algorithms implemented in garbage collected languages can actually be faster than their equivalents using manual heap allocation. A major reason for this is that the garbage collector allows the runtime system to amortize allocation and deallocation operations in a potentially advantageous fashion.

That said, I’ve written a lot of C# and a lot of C++, and I’ve run a lot of benchmarks. In my experience, C++ is a lot faster than C#, in two ways: (1) if you take some code that you’ve written in C#, port it to C++ the native code tends to be faster. Quão rápido? Well, it varies a whole lot, but it’s not uncommon to see a 100% speed improvement. (2) In some cases, garbage collection can massively slow down a managed application. The .NET CLR does a terrible job with large heaps (say, > 2GB), and can end up spending a lot of time in GC–even in applications that have few–or even no–objects of intermediate life spans.

Of course, in most cases that I’ve encounted, managed languages are fast enough, by a long shot, and the maintenance and coding tradeoff for the extra performance of C++ is simply not a good one.

Here’s an interesting benchmark http://zi.fi/shootout/

Actually Sun’s HotSpot JVM uses “mixed-mode” execution. It interprets the method’s bytecode until it determines (usually through a counter of some sort) that a particular block of code (method, loop, try-catch block, etc.) is going to be executed a lot, then it JIT compiles it. The time required to JIT compile a method often takes longer than if the method were to be interpreted if it is a seldom run method. Performance is usually higher for “mixed-mode” because the JVM does not waste time JITing code that is rarely, if ever, run. C# and .NET do not do this. .NET JITs everything which, often times, wastes time.

Go read about HP Labs’ Dynamo , an interpreter for PA-8000 that runs on PA-8000, and often runs programs faster than they do natively. Then it won’t seem at all surprising!

Don’t think of it as an “intermediate step” — running a program involves lots of other steps already, in any language.

It often comes down to:

  • programs have hot-spots, so even if you’re slower running 95% of the body of code you have to run, you can still be performance-competitive if you’re faster at the hot 5%

  • a HLL knows more about your intent than a LLL like C/C++, and so can generate more optimized code (OCaml has even more, and in practice is often even faster)

  • a JIT compiler has a lot of information that a static compiler doesn’t (like, the actual data you happen to have this time)

  • a JIT compiler can do optimizations at run-time that traditional linkers aren’t really allowed to do (like reordering branches so the common case is flat, or inlining library calls)

All in all, C/C++ are pretty lousy languages for performance: there’s relatively little information about your data types, no information about your data, and no dynamic runtime to allow much in the way of run-time optimization.

You might get short bursts when Java or CLR is faster than C++, but overall the performance is worse for the life of the application: see http://www.codeproject.com/KB/dotnet/RuntimePerformance.aspx for some results for that.

My understanding is that C/C++ produces native code to run on a particular machine architecture. Conversely, languages like Java and C# run on top of a virtual machine which abstracts away the native architecture. Logically it would seem impossible for Java or C# to match the speed of C++ because of this intermediate step, however I’ve been told that the latest compilers (“hot spot”) can attain this speed or even exceed it.

That is illogical. The use of an intermediate representation does not inherently degrade performance. For example, llvm-gcc compiles C and C++ via LLVM IR (which is a virtual infinite-register machine) to native code and it achieves excellent performance (often beating GCC).

Perhaps this is more of a compiler question than a language question, but can anyone explain in plain English how it is possible for one of these virtual machine languages to perform better than a native language?

aqui estão alguns exemplos:

  • Virtual machines with JIT compilation facilitate run-time code generation (eg System.Reflection.Emit on .NET) so you can compile generated code on-the-fly in languages like C# and F# but must resort to writing a comparatively-slow interpreter in C or C++. For example, to implement regular expressions.

  • Parts of the virtual machine (eg the write barrier and allocator) are often written in hand-coded assembler because C and C++ do not generate fast enough code. If a program stresses these parts of a system then it could conceivably outperform anything that can be written in C or C++.

  • Dynamic linking of native code requires conformance to an ABI that can impede performance and obviates whole-program optimization whereas linking is typically deferred on VMs and can benefit from whole-program optimizations (like .NET’s reified generics).

I’d also like to address some issues with paercebal’s highly-upvoted answer above (because someone keeps deleting my comments on his answer) that presents a counter-productively polarized view:

The code processing will be done at compilation time…

Hence template metaprogramming only works if the program is available at compile time which is often not the case, eg it is impossible to write a competitively performant regular expression library in vanilla C++ because it is incapable of run-time code generation (an important aspect of metaprogramming).

…playing with types is done at compile time…the equivalent in Java or C# is painful at best to write, and will always be slower and resolved at runtime even when the types are known at compile time.

In C#, that is only true of reference types and is not true for value types.

No matter the JIT optimization, nothing will go has fast as direct pointer access to memory…if you have contiguous data in memory, accessing it through C++ pointers (ie C pointers… Let’s give Caesar its due) will goes times faster than in Java/C#.

People have observed Java beating C++ on the SOR test from the SciMark2 benchmark precisely because pointers impede aliasing-related optimizations.

Also worth noting that .NET does type specialization of generics across dynamically-linked libraries after linking whereas C++ cannot because templates must be resolved before linking. And obviously the big advantage generics have over templates is comprehensible error messages.

On top of what some others have said, from my understanding .NET and Java are better at memory allocation. Eg they can compact memory as it gets fragmented while C++ cannot (natively, but it can if you’re using a clever garbage collector).

For anything needing lots of speed, the JVM just calls a C++ implementation, so it’s a question more of how good their libs are than how good the JVM is for most OS related things. Garbage collection cuts your memory in half, but using some of the fancier STL and Boost features will have the same effect but with many times the bug potential.

If you are just using C++ libraries and lots of its high level features in a large project with many classs you will probably wind up slower than using a JVM. Except much more error prone.

However, the benefit of C++ is that it allows you to optimize yourself, otherwise you are stuck with what the compiler/jvm does. If you make your own containers, write your own memory management that’s aligned, use SIMD, and drop to assembly here and there, you can speed up at least 2x-4x times over what most C++ compilers will do on their own. For some operations, 16x-32x. That’s using the same algorithms, if you use better algorithms and parallelize, increases can be dramatic, sometimes thousands of times faster that commonly used methods.

I look at it from a few different points.

  1. Given infinite time and resources, will managed or unmanaged code be faster? Clearly, the answer is that unmanaged code can always at least tie managed code in this aspect – as in the worst case, you’d just hard-code the managed code solution.
  2. If you take a program in one language, and directly translate it to another, how much worse will it perform? Probably a lot, for any two languages. Most languages require different optimizations and have different gotchas. Micro-performance is often a lot about knowing these details.
  3. Given finite time and resources, which of two languages will produce a better result? This is the most interesting question, as while a managed language may produce slightly slower code (given a program reasonably written for that language), that version will likely be done sooner, allowing for more time spent on optimization.

A very short answer: Given a fixed budget you will achieve better performing java application than a C++ application (ROI considerations) In addition Java platform has more decent profilers, that will help you pinpoint your hotspots more quickly