JIT vs. Compilador Estático
Como já foi dito nas postagens anteriores, o JIT pode compilar IL / bytecode no código nativo em tempo de execução. O custo disso foi mencionado, mas não a sua conclusão:
O JIT tem um grande problema: não é possível compilar tudo: a compilação do JIT leva tempo, portanto o JIT compilará apenas algumas partes do código, enquanto um compilador estático produzirá um binário nativo completo: Para alguns tipos de programas, o estático O compilador simplesmente superará facilmente o JIT.
É claro que C # (ou Java ou VB) geralmente é mais rápido para produzir uma solução viável e robusta do que o C ++ (apenas porque o C ++ possui semântica complexa e a biblioteca padrão do C ++, embora interessante e poderosa, é bastante ruim quando comparada à versão completa escopo da biblioteca padrão do .NET ou Java); portanto, normalmente, a diferença entre C ++ e .NET ou Java JIT não será visível para a maioria dos usuários e, para os binários críticos, bem, você ainda pode chamar o processamento C ++ de C # ou Java (mesmo que esse tipo de chamada nativa possa ser bastante caro por si só) ...
Metaprogramação em 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 # imediatamente, que é a metaprogramação de modelos: O processamento do código será feito no tempo de compilação (aumentando assim o tempo de compilação), resultando em zero (ou quase zero) tempo de execução.
Eu ainda tenho um efeito na vida real sobre isso (eu joguei apenas com conceitos, mas até então, a diferença era segundos de execução para JIT e zero para C ++), mas vale a pena mencionar, juntamente com a metaprogramação do modelo de fato não é trivial...
Edit 2011-06-10: No C ++, a reprodução de tipos é feita em tempo de compilação, o que significa produzir código genérico que chama código não genérico (por exemplo, um analisador genérico de string para o tipo T, chamando a API da biblioteca padrão para os tipos T que reconhece, e tornar o analisador facilmente extensível pelo usuário) é muito fácil e muito eficiente, enquanto o equivalente em Java ou C # é doloroso na melhor das hipóteses de escrever e sempre será mais lento e resolvido em tempo de execução, mesmo quando os tipos são conhecidos em tempo de compilação, ou seja, sua única esperança é que o JIT incline tudo.
...
Edit 2011-09-20: A equipe por trás do Blitz ++ ( Homepage , Wikipedia ) seguiu esse caminho e, aparentemente, seu objetivo é alcançar o desempenho do FORTRAN em cálculos científicos, movendo o máximo possível da execução do tempo de execução para o tempo de compilação, via metaprogramação de modelos C ++ . Assim, o " eu ainda assim ver um efeito da vida real neste " parte eu escrevi acima, aparentemente, não existe na vida real.
Uso de memória C ++ nativa
O C ++ possui um uso de memória diferente de Java / C # e, portanto, possui vantagens / falhas diferentes.
Independentemente da otimização do JIT, nada será rápido como o acesso direto do ponteiro à memória (vamos ignorar por um momento os caches do processador, etc.). Portanto, se você tiver dados contíguos na memória, acessá-los através de ponteiros C ++ (isto é, ponteiros C ... Vamos dar o devido ao Caesar) passará vezes mais rápido do que em Java / C #. E o C ++ possui RAII, o que facilita muito o processamento do que em C # ou mesmo em Java. C ++ não precisa using
escopo a existência de seus objetos. E o C ++ não possui uma finally
cláusula. Isso não é um erro.
:-)
E, apesar das estruturas primitivas do C #, os objetos C ++ "na pilha" não custarão nada na alocação e destruição e não precisarão de GC para trabalhar em um encadeamento independente para fazer a limpeza.
Quanto à fragmentação da memória, os alocadores de memória em 2008 não são os antigos alocadores de memória de 1980 que geralmente são comparados a uma alocação GC: C ++ não pode ser movida na memória, é verdade, mas, como em um sistema de arquivos Linux: quem precisa de disco rígido desfragmentar quando a fragmentação não acontece? O uso do alocador correto para a tarefa correta deve fazer parte do kit de ferramentas do desenvolvedor C ++. Agora, escrever alocadores não é fácil e, em seguida, a maioria de nós tem coisas melhores para fazer e, na maior parte do tempo, RAII ou GC é mais que suficiente.
Editar 2011-10-04: Para exemplos sobre alocadores eficientes: Nas plataformas Windows, desde o Vista, o Heap de fragmentação baixa é ativado por padrão. Para versões anteriores, o LFH pode ser ativado chamando a função WinAPI HeapSetInformation ). Em outros sistemas operacionais, alocadores alternativos são fornecidos (consultehttps://secure.wikimedia.org/wikipedia/en/wiki/Malloc para obter uma lista)
Agora, o modelo de memória está se tornando um pouco mais complicado com o aumento da tecnologia multicore e multithreading. Nesse campo, acho que o .NET tem a vantagem, e o Java, disseram-me, manteve o nível superior. É fácil para alguns hackers "simples" elogiarem seu código "próximo à máquina". Mas agora, é muito mais difícil produzir melhor montagem manualmente do que deixar o compilador trabalhar. Para C ++, o compilador geralmente se tornou melhor que o hacker desde uma década. Para C # e Java, isso é ainda mais fácil.
Ainda assim, o novo C ++ 0x padrão imporá um modelo de memória simples aos compiladores C ++, que padronizará (e, portanto, simplificará) o código efetivo de multiprocessamento / paralelo / encadeamento em C ++, e tornará as otimizações mais fáceis e seguras para os compiladores. Mas então, veremos em alguns anos se suas promessas são cumpridas.
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 do .NET e descobri que o compilador estático é muito importante de qualquer maneira. Tão importante quanto o JIT.
O mesmo código compilado em C ++ / CLI (ou seu ancestral, C ++ gerenciado) pode ser mais rápido que o mesmo código produzido em C # (ou VB.NET, cujo compilador produz a mesma IL que C #).
Como o compilador estático C ++ era muito melhor para produzir código já otimizado do que os C #.
Por exemplo, a função embutida no .NET é limitada a funções cujo código de bytes é menor ou igual a 32 bytes de comprimento. Portanto, algum código em C # produzirá um acessador de 40 bytes, que nunca será incorporado pelo JIT. O mesmo código em C ++ / CLI produzirá um acessador de 20 bytes, que será incorporado pelo JIT.
Outro exemplo são as variáveis temporárias, que são simplesmente compiladas pelo compilador C ++ enquanto ainda são mencionadas na IL produzida pelo compilador C #. A otimização da compilação estática em C ++ resultará em menos código, autorizando uma otimização JIT mais agressiva novamente.
A razão para isso foi especulada como o fato de o compilador C ++ / CLI lucrar com as vastas técnicas de otimização do compilador nativo C ++.
Conclusão
Eu amo C ++.
Mas, na minha opinião, C # ou Java são uma aposta melhor. Não porque eles são mais rápidos que o C ++, mas porque quando você soma suas qualidades, eles acabam sendo mais produtivos, precisando de menos treinamento e tendo bibliotecas padrão mais completas que o C ++. E, como na maioria dos programas, suas diferenças de velocidade (de uma maneira ou de outra) serão desprezíveis ...
Editar (06/06/2011)
Minha experiência em C # / .NET
Eu tenho agora 5 meses de codificação C # profissional quase exclusiva (que adiciona 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 (legal !!!! Tanto por XAML quanto por C # bruto. O WPF é tão fácil que acredito que o Swing simplesmente não se compara 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 ++. Os motivos não faltam, mas podem ser resumidos por:
- 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 )
- O RAII permanece incomparável (o GC ainda pode vazar (sim, eu tive que lidar com esse problema) e só lidará com a memória. Mesmo os C #
using
não são tão fáceis e poderosos porque é difícil escrever implementações corretas do Dispose .
- C #
readonly
e Java final
não são tão úteis quanto os de C ++const
( não há como você expor dados complexos somente de leitura (uma Árvore de Nós, por exemplo) em C # sem um tremendo trabalho, embora seja um recurso interno do C ++. Dados imutáveis são uma solução interessante , mas nem tudo pode ser imutável, portanto, de longe, não é o suficiente ).
Portanto, o C # permanece uma linguagem agradável, desde que você queira algo que funcione, mas uma linguagem frustrante no momento em que você deseja algo que sempre e com segurança funcione.
O Java é ainda mais frustrante, pois tem os mesmos problemas que o C # e mais: como não possui a using
palavra-chave C # , um colega muito experiente gastou muito tempo para garantir que seus recursos fossem liberados corretamente, enquanto o equivalente em C ++ teria foi fácil (usando destruidores e ponteiros inteligentes).
Então, acho que o ganho de produtividade do C # / Java é visível para a maioria dos códigos ... até o dia em que você precisar que o código seja o mais perfeito possível. Nesse dia, você conhecerá a dor. (você não vai acreditar no que é solicitado em nossos aplicativos de servidor e GUI ...).
Sobre Java e C ++ do lado do servidor
Eu mantive contato com as equipes de servidores (trabalhei 2 anos entre elas, antes de retornar à equipe da GUI), do outro lado do prédio, e aprendi algo interessante.
Nos últimos anos, a tendência era ter os aplicativos de servidor Java destinados a substituir os aplicativos antigos de servidor C ++, pois o Java possui muitas estruturas / ferramentas e é fácil de manter, implantar etc. etc.
... Até o problema da baixa latência elevar sua cabeça feia nos últimos meses. Então, os aplicativos de servidor Java, independentemente da otimização tentada por nossa equipe qualificada de Java, perderam de forma simples e limpa a corrida contra o antigo servidor C ++ não otimizado.
Atualmente, a decisão é manter os servidores Java para uso comum, onde o desempenho ainda é importante, não está preocupado com o destino de baixa latência e otimizar agressivamente os aplicativos do servidor C ++, que são mais rápidos, para necessidades de baixa latência e ultra-baixa latência.
Conclusão
Nada é tão simples quanto o esperado.
Java e ainda mais C # são linguagens legais, com extensas bibliotecas e estruturas padrão, nas quais 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, recursos poderosos de linguagem e segurança absoluta, Java e C # dificultam a conquista dos últimos percentuais de qualidade ausentes mas críticos, que você precisa manter-se acima da concorrência.
É como se você precisasse de menos tempo e menos desenvolvedores 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 um código de qualidade excelente a perfeito, subitamente ficou mais fácil e rápido obter os resultados certo em C ++.
Obviamente, essa é minha própria percepção, talvez limitada a nossas necessidades específicas.
Mas, ainda assim, é o que acontece hoje, tanto nas equipes da GUI quanto nas equipes do lado do servidor.
Obviamente, atualizarei este post se algo novo acontecer.
Editar (22/06/2011)
"Descobrimos que, em relação ao desempenho, o C ++ vence por uma grande margem. No entanto, também exigiu os mais extensos esforços de ajuste, 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 em termos de desempenho. Especificamente, os efeitos em torno da coleta de lixo foram complicados e muito difíceis de ajustar ".
Fontes:
Editar (20/09/2011)
"A palavra certa no Facebook é que ' código C ++ razoavelmente escrito é rápido ' , o que ressalta o enorme esforço gasto na otimização de 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 outras línguas]. "
- Herb Sutter em // build / , citando Andrei Alexandrescu
Fontes: