Por que seria possível que o Java fosse mais rápido que o C ++?

80

Às vezes, o Java supera o C ++ em benchmarks. Obviamente, às vezes o C ++ supera.

Veja os seguintes links:

Mas como isso é possível? Surpreende minha mente que o código de código interpretado possa ser mais rápido do que uma linguagem compilada.

Alguém pode me explicar? Obrigado!

Deets McGeets
fonte
2
Você pode dar uma olhada em shootout.alioth.debian.org/u32/… para ver o tipo de problemas que são mais rápidos no java / c ++ ... Veja o padrão de problemas e não esses problemas específicos ...
c0da
2
Consulte Por que o java tinha a reputação de ser lento? para muitos detalhes sobre esse tópico.
Péter Török
11
É ilegal (Seção 10.101.04.2c) produzir uma Java VM com desempenho mais rápido que um binário executável produzido com C ++.
Mateen Ulhaq
1
@muntoo O que você quer dizer com isso? Seção 10.101.04.2c de quê?
Highland Mark
3
@HighlandMark Você não é um membro do círculo. Você não deve saber o que se passa dentro do círculo. O círculo é absoluto - as leis do círculo substituem as da natureza. Você não pode desafiar nem questionar o círculo. Eu sou o círculo e o círculo sou eu.
Mateen Ulhaq

Respostas:

108

Primeiro, a maioria das JVMs inclui um compilador, portanto, "bytecode interpretado" é realmente muito raro (pelo menos no código de referência - não é tão raro na vida real, onde seu código geralmente é mais do que alguns loops triviais que são repetidos com muita frequência )

Segundo, um bom número de benchmarks envolvidos parece ser bastante tendencioso (seja por intenção ou incompetência, não sei dizer). Apenas por exemplo, anos atrás, observei alguns dos códigos-fonte vinculados a partir de um dos links que você postou. Tinha código como este:

  init0 = (int*)calloc(max_x,sizeof(int));
  init1 = (int*)calloc(max_x,sizeof(int));
  init2 = (int*)calloc(max_x,sizeof(int));
  for (x=0; x<max_x; x++) {
    init2[x] = 0;
    init1[x] = 0;
    init0[x] = 0;
  }

Como callocfornece memória que já está zerada, usar o forloop para zerá-lo novamente é obviamente inútil. Isso foi seguido (se a memória servir) preenchendo a memória com outros dados de qualquer maneira (e nenhuma dependência de zerar), portanto todo o zeramento era completamente desnecessário. Substituir o código acima por um simples malloc(como qualquer pessoa sã teria usado no início) melhorou a velocidade da versão C ++ o suficiente para vencer a versão Java (por uma margem bastante ampla, se a memória servir).

Considere (por outro exemplo) a methcallreferência usada na entrada do blog em seu último link. Apesar do nome (e como as coisas podem parecer), a versão C ++ disso não está realmente medindo muito a sobrecarga de chamada de método. A parte do código que acaba sendo crítica está na classe Toggle:

class Toggle {
public:
    Toggle(bool start_state) : state(start_state) { }
    virtual ~Toggle() {  }
    bool value() {
        return(state);
    }
    virtual Toggle& activate() {
        state = !state;
        return(*this);
    }
    bool state;
};

A parte crítica acaba por ser a state = !state;. Considere o que acontece quando alteramos o código para codificar o estado como um em intvez de bool:

class Toggle {
    enum names{ bfalse = -1, btrue = 1};
    const static names values[2];
    int state;

public:
    Toggle(bool start_state) : state(values[start_state]) 
    { }
    virtual ~Toggle() {  }
    bool value() {  return state==btrue;    }

    virtual Toggle& activate() {
        state = -state;
        return(*this);
    }
};

Essa pequena alteração melhora a velocidade geral em uma margem de 5: 1 . Embora o benchmark visasse medir o tempo de chamada do método, na realidade, o que estava medindo era o tempo de conversão entre inte bool. Eu certamente concordaria que a ineficiência mostrada pelo original é lamentável - mas, dado o quão raramente parece surgir no código real, e a facilidade com que ele pode ser corrigido quando / se surgir, tenho dificuldade em pensar. disso significa muito.

Caso alguém decida executar novamente os benchmarks envolvidos, devo acrescentar que há uma modificação quase igualmente trivial na versão Java que produz (ou pelo menos uma vez produzida - eu não reexecutei os testes com um JVM recente para confirmar que ainda fazem) uma melhoria bastante substancial na versão Java também. A versão Java possui um NthToggle :: Activ () que se parece com isso:

public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
    this.state = !this.state;
    this.counter = 0;
}
return(this);
}

Alterar isso para chamar a função base, em vez de manipular this.statediretamente, oferece uma melhoria substancial na velocidade (embora não seja suficiente para acompanhar a versão C ++ modificada).

Então, acabamos com uma suposição falsa sobre códigos de bytes interpretados versus alguns dos piores benchmarks (já) que eu já vi. Nem está dando um resultado significativo.

Minha própria experiência é que, com programadores igualmente experientes prestando a mesma atenção à otimização, o C ++ vence o Java com mais frequência - mas (pelo menos entre esses dois), a linguagem raramente faz tanta diferença quanto os programadores e o design. Os benchmarks citados nos dizem mais sobre a (in) competência / (des) honestidade de seus autores do que sobre os idiomas que pretendem ser benchmark.

[Edit: Como está implícito em um lugar acima, mas nunca foi tão diretamente quanto eu provavelmente deveria), os resultados que eu estou citando são os que obtive quando testei isso ~ 5 anos atrás, usando implementações C ++ e Java atuais na época . Não executei novamente os testes com as implementações atuais. Uma olhada, no entanto, indica que o código não foi corrigido; portanto, tudo o que teria mudado seria a capacidade do compilador de encobrir os problemas no código.]

Se ignorarmos os exemplos de Java, no entanto, é realmente possível que o código interpretado seja executado mais rápido que o código compilado (embora difícil e um tanto incomum).

A maneira usual de isso acontecer é que o código que está sendo interpretado é muito mais compacto que o código da máquina ou está sendo executado em uma CPU que possui um cache de dados maior que o cache de código.

Nesse caso, um pequeno intérprete (por exemplo, o intérprete interno de uma quarta implementação) pode caber inteiramente no cache de código, e o programa que está interpretando se encaixa inteiramente no cache de dados. O cache normalmente é mais rápido que a memória principal por um fator de pelo menos 10 e geralmente muito mais (um fator de 100 não é mais particularmente raro).

Portanto, se o cache for mais rápido que a memória principal por um fator N e forem necessárias menos de N instruções de código de máquina para implementar cada código de byte, o código de byte deverá vencer (estou simplificando, mas acho que a ideia geral ainda deve ser aparente).

Jerry Coffin
fonte
26
+1, reconhecimento total. Especialmente "a linguagem raramente fará tanta diferença quanto os programadores e o design" - você frequentemente tropeça em problemas nos quais pode otimizar o algoritmo, por exemplo, melhorar o big-O que dará muito mais impulso do que o melhor compilador poderia.
schnaader
1
"Caso alguém decida executar novamente os benchmarks envolvidos ..." NÃO! Em 2005, essas tarefas antigas foram descartadas e substituídas pelas tarefas agora mostradas no jogo de benchmarks. Se alguém quer re-executar alguns programas, em seguida, volte a executar os programas atuais para as tarefas atuais mostrados no benchmarks home page jogo shootout.alioth.debian.org
igouy
@igouy: Algumas pessoas podem simplesmente querer confirmar / negar os resultados dos benchmarks executados, com o mínimo de correções necessárias para, pelo menos, proporcionar-lhes um relacionamento mínimo com a realidade. Ao mesmo tempo, você está basicamente certo: os benchmarks em questão são tão ruins que apenas corrigir os erros mais óbvios não ajuda muito.
Jerry Coffin
E é por isso que, em 2005, eles foram descartados e substituídos pelas tarefas agora mostradas no jogo de benchmarks. Pessoas que não conhecem melhor re-executam esses programas antigos.
igouy 26/09/11
13
+1 Não gosto de pessoas que codificam C ++ no estilo C ou Java e depois afirmam que Java é superior. isenção de responsabilidade: eu não chamo nenhum idioma de superior, mas escrever código C ++ ruim em um estilo que possa ser perfeitamente adequado a outro idioma não torna os dois idiomas comparáveis.
Christian Rau
112

O C / C ++ feito à mão por um especialista com tempo ilimitado será pelo menos tão rápido ou mais rápido que o Java. Por fim, o próprio Java é escrito em C / C ++, para que você possa fazer tudo o que o Java faz, se estiver disposto a colocar esforços de engenharia suficientes.

Na prática, no entanto, o Java geralmente é executado muito rápido pelos seguintes motivos:

  • Compilação JIT - embora as classes Java sejam armazenadas como código de bytes, isso geralmente é compilado no código nativo pelo compilador JIT quando o programa é iniciado. Uma vez compilado, é um código nativo puro - portanto, teoricamente, pode-se esperar que ele execute C / C ++ tão compilado quando o programa estiver em execução por tempo suficiente (ou seja, depois que toda a compilação JIT for concluída)
  • A coleta de lixo em Java é extremamente rápida e eficiente - o Hotspot GC é provavelmente a melhor implementação geral de GC do mundo. É o resultado de muitos anos de esforço especializado da Sun e de outras empresas. Praticamente qualquer sistema de gerenciamento de memória complexo que você utilize em C / C ++ será pior. É claro que você pode escrever esquemas básicos de gerenciamento de memória bastante rápidos / leves em C / C ++, mas eles não serão tão versáteis quanto um sistema completo de GC. Como a maioria dos sistemas modernos precisa de gerenciamento de memória complexo, o Java tem uma grande vantagem para situações do mundo real.
  • Melhor direcionamento da plataforma - adiando a compilação para a inicialização do aplicativo (compilação JIT etc.), o compilador Java pode tirar proveito do fato de conhecer o processador exato em que está executando. Isso pode permitir algumas otimizações muito benéficas que você não seria capaz de fazer no código C / C ++ pré-compilado que precisa direcionar um conjunto de instruções do processador "menor denominador comum".
  • Estatísticas de tempo de execução - como a compilação JIT é feita em tempo de execução, ela pode coletar estatísticas enquanto o programa está em execução, o que possibilita melhores otimizações (por exemplo, sabendo a probabilidade de uma ramificação específica). Isso pode permitir que os compiladores Java JIT produzam código melhor que os compiladores C / C ++ (que precisam "adivinhar" a ramificação mais provável com antecedência, uma suposição que pode estar errada).
  • Bibliotecas muito boas - o tempo de execução Java contém um host de bibliotecas muito bem escritas com bom desempenho (especialmente para aplicativos do lado do servidor). Geralmente, são melhores do que você poderia escrever por conta própria ou obter facilmente com o C / C ++.

Ao mesmo tempo, o C / C ++ também possui algumas vantagens:

  • Mais tempo para otimizações avançadas - a compilação do C / C ++ é feita uma vez e, portanto, pode levar um tempo considerável realizando otimizações avançadas, se você a configurar. Não há nenhuma razão teórica para que o Java não possa fazer o mesmo, mas, na prática, você deseja que o Java compile código JIT de maneira relativamente rápida, portanto o compilador JIT tende a se concentrar em otimizações "mais simples".
  • Instruções que não são expressáveis ​​no bytecode - enquanto o bytecode Java é de uso geral, ainda existem algumas coisas que você pode fazer em um nível baixo que não pode ser feito no bytecode (a aritmética do ponteiro não verificado é um bom exemplo!). Ao (ab) usar esse tipo de truque, você pode obter algumas vantagens de desempenho
  • Menos restrições de "segurança" - o Java faz um trabalho extra para garantir que os programas sejam seguros e confiáveis. Exemplos são verificações de limites em matrizes, certas garantias de simultaneidade, verificações de ponteiro nulo, segurança de tipo em elencos etc.

No geral:

  • Java e C / C ++ podem atingir velocidades semelhantes
  • O C / C ++ provavelmente tem uma ligeira vantagem em circunstâncias extremas (não é surpreendente que os desenvolvedores de jogos AAA ainda o prefiram, por exemplo)
  • Na prática, isso dependerá de como os diferentes fatores listados acima se equilibrarão para sua aplicação específica.
Mikera
fonte
9
Anúncio "mais tempo para otimizações em C ++": esse é um dos ajustes que a VM Oracle faz quando você escolhe a VM Server: aceita um custo inicial mais alto para permitir um desempenho mais alto a longo prazo. A VM do cliente, no entanto, é ajustada para otimizar o tempo de inicialização. Portanto, essa distinção existe até em Java.
Joachim Sauer
8
-1: um compilador C ++ pode levar muito mais tempo (horas, literalmente, para uma grande biblioteca) para criar um binário muito otimizado. O compilador Java JIT não pode demorar muito, mesmo a versão "server". Eu duvido seriamente que o compilador Java JIT possa executar a Otimização de Programa Inteiro da maneira que o compilador MS C ++ faz.
quant_dev 26/09/11
20
@quant_dev: claro, mas não foi exatamente isso que eu disse na minha resposta como uma vantagem de C ++ (mais tempo para fazer otimização avançada)? Então, por que o -1?
Mikera # 26/11
13
A coleta de lixo não é uma vantagem de velocidade para Java. É apenas uma vantagem de velocidade se você é um programador C ++ que não sabe o que está fazendo. Se tudo o que você está verificando é quão rápido você pode alocar, então sim, o coletor de lixo vencerá. No entanto, o desempenho geral do programa ainda pode ser melhor gerenciando manualmente a memória.
Billy ONeal
4
... Mas com o C ++, você sempre pode colocar teoricamente uma "camada semelhante a JIT" que faz otimizações de ramificação semelhantes em tempo de execução, mantendo a velocidade bruta de um programa em C ++. (Teoricamente. :())
Mateen Ulhaq 26/09/11
19

O tempo de execução Java não está interpretando o bytecode. Em vez disso, ele usa o que é chamado de compilação Just In Time . Basicamente, à medida que o programa é executado, ele pega o bytecode e o converte em código nativo otimizado para a CPU específica.

GrandmasterB
fonte
Na prática, sim. Em princípio, isso depende - as máquinas virtuais Java antigas usavam intérpretes de código de código e você provavelmente ainda pode encontrar VMs de interpretação de código de código de código se você se esforçar o bastante.
precisa saber é o seguinte
10
@ Steve314: mas as VMs puramente interpretadoras não serão as que superam o C ++, portanto, não são realmente relevantes para esta questão.
Joachim Sauer
O compilador JIT também pode otimizar dinamicamente para o uso específico do código, o que não é possível com o código compilado estaticamente.
starblue
2
@starblue, bem, é um pouco possível com uma compilação estática - veja a otimização guiada por perfil.
SK-logic
19

Todas as coisas são iguais, você poderia dizer: não, o Java nunca deve ser mais rápido . Você sempre pode implementar Java em C ++ a partir do zero e, assim, obter pelo menos o mesmo desempenho. Na prática, no entanto:

  • O JIT compila o código na máquina do usuário final, permitindo otimizar a CPU exata que eles estão executando. Embora exista uma sobrecarga aqui para a compilação, pode valer a pena para aplicativos intensivos. Geralmente, os programas da vida real não são compilados para a CPU que você está usando.
  • O compilador Java pode muito bem ser melhor em otimizar automaticamente as coisas do que um compilador C ++. Ou talvez não, mas no mundo real, as coisas nem sempre são perfeitas.
  • O comportamento do desempenho pode variar devido a outros fatores, como coleta de lixo. Em C ++, você normalmente chama o destruidor imediatamente quando feito com um objeto. Em Java, você simplesmente libera a referência, atrasando a destruição real. Este é outro exemplo de diferença que não está aqui nem ali, em termos de desempenho. Obviamente, você pode argumentar que poderia implementar o GC em C ++ e acabar com ele, mas a realidade é que poucas pessoas fazem / querem / podem.

Como um aparte, isso me lembra o debate sobre C nas décadas de 80 e 90. Todo mundo estava pensando "C pode ser tão rápido quanto a montagem?". Basicamente, a resposta foi: não no papel, mas, na realidade, o compilador C criou um código mais eficiente que 90% dos programadores de montagem (bem, depois que amadureceu um pouco).

Daniel B
fonte
2
Em relação ao GC, não é apenas que o GC possa atrasar a destruição de objetos (o que não deve importar a longo prazo); o fato é que, com os GCs modernos, a alocação / desalocação de objetos de vida curta é extremamente barata em Java, em comparação com o C ++.
Péter Török
@ PéterTörök sim, você está certo, bom ponto.
Daniel B
9
@ PéterTörök Mas, em C ++, os objetos de curta duração são frequentemente colocados na pilha, o que, por sua vez, é muito mais rápido do que qualquer heap que o Java pode usar no GC.
quant_dev 26/09/11
@quant_dev, você esqueceu outro efeito significativo do GC: compactação. Portanto, eu não teria tanta certeza de qual caminho é mais rápido.
SK-logic,
3
@DonalFellows O que faz você pensar que preciso me preocupar com o gerenciamento de memória em C ++? Na maioria das vezes eu não. Existem padrões simples que você precisa aplicar, que são diferentes do Java, mas é isso.
quant_dev 26/09/11
10

Mas a alocação é apenas metade do gerenciamento de memória - a desalocação é a outra metade. Acontece que, para a maioria dos objetos, o custo direto da coleta de lixo é - zero. Isso ocorre porque um coletor de cópias não precisa visitar ou copiar objetos mortos, apenas objetos ativos. Portanto, objetos que se tornam lixo logo após a alocação não contribuem com carga de trabalho para o ciclo de coleta.

...

As JVMs são surpreendentemente boas em descobrir coisas que costumamos assumir que apenas o desenvolvedor poderia saber. Ao permitir que a JVM escolha entre alocação de pilha e alocação de heap caso a caso, podemos obter os benefícios de desempenho da alocação de pilha sem fazer com que o programador se agonize sobre a alocação na pilha ou no heap.

http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html

Landei
fonte
Essa é apenas uma pequena parte do quadro geral, mas bastante relevante, no entanto.
Joachim Sauer
2
Eu gosto de como a substância disso é: java é para noobs, confie no GC mágico, ele sabe melhor.
Morg.
1
@Org: Ou você pode ler dessa maneira: Java é para pessoas que gostam de fazer as coisas, em vez de perder tempo com pequenas correções e gerenciamento manual de memória.
Landei
4
@Landei Acho que seu comentário teria muito mais credibilidade se qualquer base de código decente e duradoura e amplamente utilizada tivesse sido escrita em Java. No meu mundo, os sistemas operacionais reais são escritos em C, o postgreSQL é escrito em C, assim como as ferramentas mais importantes que seriam realmente difíceis de reescrever. O Java foi (e essa é a versão oficial) para permitir que pessoas menos qualificadas programem em rebanhos e, no entanto, alcancem resultados tangíveis.
Morg.
1
@Org Acho muito estranho como você parece se concentrar apenas nos sistemas operacionais. Isso simplesmente não pode ser uma boa medida, por várias razões. Primeiro, os requisitos dos sistemas operacionais são crucialmente diferentes da maioria dos outros softwares; depois, você tem o princípio do Panda Thumb (quem quer reescrever um sistema operacional completo em outro idioma, quem quer criar seu próprio sistema operacional, se houver alternativas funcionais e até gratuitas?) e o terceiro outro software usa os recursos do sistema operacional; portanto, não há necessidade de escrever um driver de disco, gerenciador de tarefas, etc. Se você não pode fornecer argumentos melhores (não baseados inteiramente em sistemas operacionais), soa como um odiador.
Landei
5

Enquanto um programa Java completamente otimizado raramente supera um programa C ++ completamente otimizado, diferenças em coisas como gerenciamento de memória podem fazer com que muitos algoritmos sejam implementados idiomaicamente em Java mais rapidamente do que os mesmos algoritmos linguisticamente implementados em C ++.

Como @Jerry Coffin apontou, há muitos casos em que mudanças simples podem tornar o código muito mais rápido - mas muitas vezes é necessário muitos ajustes impuros em um idioma ou outro para que a melhoria de desempenho valha a pena. Isso é provavelmente o que você veria em uma boa referência que mostra o Java se saindo melhor que o C ++.

Além disso, embora geralmente não seja tão significativo, há alguma otimização de desempenho que uma linguagem JIT como Java pode fazer que C ++ não pode. O tempo de execução Java pode incluir aprimoramentos após a compilação do código, o que significa que o JIT pode potencialmente produzir código otimizado para tirar proveito dos novos (ou pelo menos diferentes) recursos da CPU. Por esse motivo, um binário Java de 10 anos pode potencialmente superar um binário C ++ de 10 anos.

Por fim, a segurança completa do tipo no quadro geral pode, em casos muito raros, oferecer melhorias extremas no desempenho. A singularidade , um sistema operacional experimental escrito quase inteiramente em uma linguagem baseada em C #, tem comunicação entre processos e multitarefa muito mais rápida devido ao fato de que não há necessidade de limites de processo de hardware ou alternâncias de contexto caras.

Rei Miyasaka
fonte
5

Postado por Tim Holloway no JavaRanch:

Aqui está um exemplo primitivo: Quando as máquinas operavam em ciclos matematicamente determinados, uma instrução de ramificação normalmente tinha 2 tempos diferentes. Um para quando o ramo foi ocupado, um para quando o ramo não foi ocupado. Geralmente, o caso sem ramificação era mais rápido. Obviamente, isso significava que era possível otimizar a lógica com base no conhecimento de qual caso era mais comum (sujeito à restrição de que o que "sabemos" nem sempre é o que realmente é o caso).

A recompilação do JIT leva esse passo adiante. Ele monitora o uso real em tempo real e inverte a lógica com base no que realmente é o caso mais comum. E vire-o novamente se a carga de trabalho mudar. Código compilado estaticamente não pode fazer isso. É assim que o Java às vezes pode superar o código assembly / C / C ++ ajustado manualmente.

Fonte: http://www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time

Thiago Negri
fonte
3
E mais uma vez, isso está errado / incompleto. Compiladores estáticos com otimização guiada por perfil podem reconhecer isso.
precisa saber é o seguinte
2
Konrad, compiladores estáticos podem mudar a lógica com base na carga de trabalho atual? Pelo que entendi, os compiladores estáticos geram código uma vez e permanece o mesmo para sempre.
Thiago Negri
2
Carga de trabalho atual, não. Mas carga de trabalho típica . A otimização guiada por perfil analisa como o seu programa é executado sob carga típica e otimiza os pontos de acesso de acordo, da mesma forma que o HotSpot JIT.
Konrad Rudolph
4

Isso ocorre porque a etapa final da geração do código da máquina ocorre de forma transparente dentro da JVM ao executar seu programa Java, em vez de explícita ao criar seu proram C ++.

Você deve considerar o fato de que as JVM modernas gastam bastante tempo compilando o código de bytes em tempo real para o código de máquina nativo para torná-lo o mais rápido possível. Isso permite que a JVM execute todos os tipos de truques do compilador que podem ser ainda melhores sabendo os dados de criação de perfil do programa que está sendo executado.

Tal como incluir automaticamente um getter, para que um JUMP-RETURN não seja necessário apenas para obter um valor, acelera as coisas.

No entanto, o que realmente permitiu programas rápidos é melhor limpar depois. O mecanismo de coleta de lixo em Java é mais rápido que o manual sem malloc em C. Muitas implementações modernas sem malloc usam um coletor de lixo por baixo.


fonte
Observe que esse material incorporado torna a inicialização da JVM maior e mais lenta até que o código melhor tenha uma chance de recuperar o atraso.
1
"Muitas implementações modernas sem malloc usam um coletor de lixo por baixo". Realmente? Eu gostaria de saber mais; Você tem alguma referência?
Sean McMillan
Obrigado. Eu estava tentando encontrar uma maneira de dizer que a JVM não contém mais apenas um compilador just in time compilando em código executável, mas um compilador de hot spot que analisa o código em execução e otimiza ainda mais como resultado. Eu compilador de uma só vez como C ++ luta para combinar isso.
Highland Mark
@SeanMcMillan, vi uma análise há algum tempo sobre o desempenho de implementações sem malloc, onde foi mencionado que o mais rápido usava um coletor de lixo por baixo. Não me lembro onde li.
Foi o GC conservador da BDW?
Demi
4

Resposta curta - não é. Esqueça, o tópico é tão antigo quanto fogo ou roda. Java ou .NET não é e não será mais rápido que C / C ++. É rápido o suficiente para a maioria das tarefas em que você não precisa pensar em otimização. Como formulários e processamento SQL, mas é aí que termina.

Para benchmarks ou aplicativos pequenos escritos por desenvolvedores incompetentes, sim, o resultado final será que o Java / .NET provavelmente estará próximo e talvez até mais rápido.

Na realidade, coisas simples como alocar memória na pilha ou simplesmente usar memzones simplesmente matam o Java / .NET no local.

O mundo da coleta de lixo está usando uma espécie de memzone com toda a contabilidade. Adicione memzone a C e C será mais rápido no local. Especialmente para os benchmarks Java vs. C "código de alto desempenho", que são assim:

for(...)
{
alloc_memory//Allocating heap in a loop is verrry good, in't it?
zero_memory//Extra zeroing, we really need it in our performance code
do_stuff//something like memory[i]++
realloc//This is lovely speedup
strlen//loop through all memory, because storing string length is soo getting old
free//Java will do that outside out timing loop, but oh well, we're comparing apples to oranges here
}//loop 100000 times

Tente usar variáveis ​​baseadas em pilha em C / C ++ (ou posicionamento novo), elas se traduzem em sub esp, 0xff, é uma única instrução x86, superada com Java - você não pode ...

Na maioria das vezes, vejo aqueles bancos em que o Java contra C ++ são comparados, e isso me leva a pensar, como? Estratégias de alocação de memória incorretas, contêineres auto-crescentes sem reservas, vários novos. Isso nem chega perto do código C / C ++ orientado para o desempenho.

Também é uma boa leitura: https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf

Codificador
fonte
1
Errado. Totalmente errado. Você não poderá superar um GC compactador com o gerenciamento manual de memória. A contagem ingênua de referências nunca será melhor do que uma boa marca. Assim que se trata de um gerenciamento de memória complicado, o C ++ é um atraso.
SK-logic,
3
@ SK-Logic: Errado, com alocação de memzones ou pilha não há alocação ou desalocação de memória. Você tem um bloco de memória e apenas escreve nele. Marque o bloco como livre com variáveis ​​voláteis, como proteção de concorrência InterlockedExchange etc., e o próximo encadeamento apenas despeja seus dados em um bloco pré-alocado, sem precisar acessar o sistema operacional para memória, se estiver livre. Com a pilha é ainda mais fácil, com a única exceção que você não pode despejar 50 MB na pilha. E a vida útil desse objeto é apenas dentro de {}.
Codificador
2
@ SK-logic: Compiladores são a correção primeiro, a performance em segundo. Mecanismos de busca, bancos de dados, sistemas de negociação em tempo real, jogos são o que eu consideraria crítico em termos de desempenho. E a maioria deles depende de estruturas achatadas. De qualquer forma, os compiladores são escritos principalmente em C / C ++. Com alocadores personalizados, eu acho. Então, novamente, não vejo problemas em usar elementos de árvore ou lista no rammap. Você acabou de usar o canal novo. Não há muita complexidade nisso.
Coder
3
@ SK-logic: Não é muito mais rápido, todo aplicativo .NET / Java que eu já vi, sempre se mostrou mais lento e um verdadeiro porco. Cada reescrita do aplicativo gerenciado no código SANE C / C ++ resultava em um aplicativo mais limpo e leve. Aplicativos gerenciados são sempre pesados. Veja VS2010 vs 2008. Mesmas estruturas de dados, mas o VS2010 é um HOG. Os aplicativos C / C ++ escritos corretamente geralmente são inicializados em milissegundos e não ficam presos nas telas de apresentação, enquanto consomem muito menos memória. A única desvantagem é que você precisa codificar com o hardware em mente, e muitas pessoas não sabem como é hoje em dia. São apenas benchmarks onde os gerentes têm uma chance.
Coder
2
sua evidência anedótica não conta. Benchmarks adequados mostram a diferença real. É especialmente estranho que você esteja se referindo aos aplicativos da GUI, vinculados às bibliotecas de GUI volumosas e abaixo do ideal. E o que é mais importante - em teoria, o limite de desempenho é muito maior para um GC implementado corretamente.
SK-logic,
2

A realidade é que eles são apenas montadores de alto nível que fazem exatamente o que o programador manda, exatamente como o programador manda na ordem exata que o programador manda. As diferenças de desempenho são tão pequenas que são irrelevantes para todos os fins práticos.

A linguagem não é "lenta", o programador escreveu um programa lento. Muito raramente, um programa escrito da melhor maneira em um idioma supera (para qualquer propósito prático) um programa que faz a mesma coisa usando o melhor caminho da linguagem alternativa, a menos que o autor do estudo queira desbastar seu machado em particular.

Obviamente, se você estiver enfrentando um caso raro, como sistemas embarcados em tempo real, a escolha do idioma pode fazer a diferença, mas com que frequência é esse o caso? e, nesses casos, com que frequência a escolha correta não é cegamente óbvia.

mattnz
fonte
2
Em teoria, uma VM JITting "ideal" deve superar o código estaticamente compilado, ajustando suas otimizações às informações de criação de perfil coletadas dinamicamente. Na prática, os compiladores JIT ainda não são tão inteligentes, mas são pelo menos capazes de produzir um código de qualidade semelhante ao de seus pares estáticos maiores e mais lentos.
SK-logic,
2

Veja os links a seguir ... Mas como isso é possível? Surpreende minha mente que o código de código interpretado possa ser mais rápido do que uma linguagem compilada.

  1. Essas postagens no blog fornecem evidências confiáveis?
  2. Essas postagens no blog fornecem evidências definitivas?
  3. Essas postagens no blog fornecem evidências sobre "bytecode interpretado"?

Keith Lea diz que existem "falhas óbvias", mas não faz nada sobre essas "falhas óbvias". Em 2005, essas tarefas antigas foram descartadas e substituídas pelas tarefas agora mostradas no jogo de benchmarks .

Keith Lea diz que ele "pegou o código de referência para C ++ e Java no agora desatualizado Great Computer Language Shootout e executou os testes", mas na verdade ele só mostra medidas para 14 dos 25 desses testes desatualizados .

Keith Lea agora diz que ele não estava tentando provar nada com o post do blog sete anos antes, mas na época ele disse "Eu estava cansado de ouvir as pessoas dizerem que o Java era lento, quando eu sei que é muito rápido ...", o que sugere Naquela época, havia algo que ele estava tentando provar.

Christian Felde diz a você "Eu não criei o código, apenas refiz os testes". como se isso o absolvesse de qualquer responsabilidade por sua decisão de divulgar medidas das tarefas e programas selecionados por Keith Lea.

As medições de até 25 programas minúsculos fornecem evidências definitivas?

Essas medidas são para programas executados como "modo misto" Java não interpretado Java - "Lembre-se de como o HotSpot funciona". Você pode descobrir facilmente quão bem o Java executa o "bytecode interpretado", porque você pode forçar o Java a interpretar apenas o bytecode - basta cronometrar alguns programas Java executados com e sem a opção -Xint.

igouy
fonte
-1

Me diverte o quão difundida é essa noção estranha de "bytecode interpretado". Você já ouviu falar da compilação JIT? Seu argumento não pode ser aplicado ao Java.

Porém, deixando a JVM de lado, há casos em que um código encadeado direto ou mesmo uma interpretação trivial de bytecode podem facilmente superar um código nativo altamente otimizado. A explicação é bem simples: o bytecode pode ser bastante compacto e caberá no seu cache minúsculo quando uma versão de código nativo do mesmo algoritmo acabar com várias falhas de cache para uma única iteração.

SK-logic
fonte
A difusão da interpretação talvez se deva a pessoas versadas em sua ciência da computação. A máquina virtual java é uma máquina que aceita java bytecode e a executa em uma máquina / não / capaz de executar java bytecode nativamente, sem ser capaz de escrever um programa nativo funcionalmente equivalente. Portanto, é um intérprete. Você pode dar a suas técnicas de armazenamento em cache qualquer nome que possa imaginar, JIT ou outro, mas é um intérprete pela definição de intérprete.
thiton
@ thiton, as chances são de que seu próprio CS-fu seja meio fraco. A JVM não faz nenhum tipo de interpretação (para pontos de acesso) - como alguém que se atreve a mencionar CS, é necessário saber o que isso significa e como a semântica operacional de uma interpretação é diferente da execução de um código nativo. Mas, provavelmente, você simplesmente não conhece CS o suficiente para distinguir compilação de interpretação.
SK-logic,
2
Umm, mas o bytecode, para ser executado, precisa ser convertido em código nativo - você não pode alimentar o bytecode Java na CPU. Portanto, o argumento do tamanho é inválido.
quant_dev 26/09/11
@quant_dev, é claro - eu disse que esse caso não tem nenhuma relação com a JVM. Você precisaria de um mecanismo de bytecode muito mais simples para que esse efeito funcionasse.
SK-logic
-1

JIT, GC e assim por diante, o C ++ pode ser muito, muito facilmente tornado muito mais lento que o Java. Isso não aparecerá nos benchmarks, mas o mesmo aplicativo criado pelo desenvolvedor Java e um desenvolvedor C ++ pode ser muito mais rápido em Java.

  • Sobrecarga do operador. Todo operador simples como "+" ou "=" pode chamar centenas de linhas de código fazendo verificações de segurança, operações de disco, registro, rastreamento e criação de perfil. E eles são tão fáceis de usar que, depois de sobrecarregar os operadores, você os utiliza de maneira natural e abundante, sem perceber como o uso se destaca.
  • Modelos. Isso não afeta tanto a velocidade quanto a memória. O uso não cauteloso de modelos levará à geração de milhões de linhas de código (alternativas para o modelo básico) sem que você as observe. Porém, o tempo de carregamento binário, o uso de memória e o uso de swap - tudo isso também age contra os benchmarks. E o uso da RAM passa pelo teto.

Quanto aos padrões avançados de herança, eles são bem parecidos - o C ++ possui alguns que o Java não possui e vice-versa, mas todos também apresentam uma sobrecarga significativa e significativa. Portanto, nenhuma vantagem especial do C ++ na programação pesada de objetos.

Mais uma ressalva: o GC pode ser mais rápido ou mais lento do que gerenciar alocações manualmente. Se você alocar muitos objetos pequenos, no ambiente do GC geralmente um alocado de memória é alocado e partes dele são despachadas conforme necessário para novos objetos. Em gerenciado - cada objeto = alocação separada leva um tempo significativo. OTOH, se você alocar muita memória de uma só vez e depois atribuir partes dela aos seus objetos manualmente ou usar poucas instâncias maiores de objetos, poderá aparecer muito mais rápido.

SF.
fonte
4
Eu discordo de ambos os pontos. Se você usa operadores ou métodos é irrelevante. Você diz que eles irão proliferar. Bobagem - não mais do que os métodos; você precisa chamá-los ou não. E os modelos não resultam em mais código do que escrever manualmente esse código específico novamente para uso múltiplo. Pode haver mais código do que no despacho de tempo de execução (funções virtuais), mas isso também será irrelevante: o desempenho das linhas de cache de instruções é mais importante em loops apertados e aqui apenas uma instanciação de modelo será usada, portanto, não haverá pressão relevante na memória devido a modelos.
Konrad Rudolph
A mentalidade usual é que os métodos são caros, os operadores são baratos. Você usa métodos quando necessário, operadores sempre que quiser economizar tempo e otimizar. Não é uma questão técnica, mas psicológica - não é que os operadores sejam "mais pesados", eles são muito mais fáceis de usar e são usados ​​com muito mais frequência. (e você pode sobrecarregar um operador comumente usado em um código pré-existente, tornando-se fazer o mesmo que o original, mais um extra - e de repente todo o código irá desacelerar significativamente.
SF.
Eu contesto que esse fato psicológico é real e, mesmo que seja, você não tem a opção : se você precisar de uma funcionalidade, use-a, seja encapsulada em um operador ou em um método. A psicologia é irrelevante para a sua escolha da semântica.
precisa saber é o seguinte
1
Truque de pergunta. Eu não iria adivinhar isso, eu mediria, agiria então . Eu nunca tive um problema com essa tática.
21911 Konrad Rudolph
1
@KonradRudolph: Isso tudo é verdade quando se trata de clareza e facilidade de escrever o código, tornando-o livre de erros e sustentável. O argumento sobre a eficiência da implementação do algoritmo ainda permanece: se você estiver prestes a escrever obj.fetchFromDatabase("key")três vezes em cinco linhas de código para a mesma chave, você pensará duas vezes se deve buscar esse valor uma vez e armazená-lo em cache em uma variável local. Se você escrever obj->"key"com ->que está sendo sobrecarregado para atuar como banco de dados de busca, você está muito mais propenso a apenas deixá-lo passar, porque o custo da operação não é aparente.
SF.
-2

De alguma forma, o Stack Exchange não aceita meus outros pontos de pilha, então ... infelizmente nenhuma resposta ...

No entanto, a segunda resposta mais votada aqui está cheia de informações erradas na minha humilde opinião.

Um aplicativo feito à mão por um especialista em C / C ++ SEMPRE será muito mais rápido que um aplicativo Java, ponto final. Não existe 'tão rápido quanto Java ou mais rápido'. é mais rápido, precisamente por causa dos itens que você cita abaixo:

Compilação JIT : Você realmente espera que um otimizador automático tenha a inteligência de um programador especialista e veja o link entre a intenção e o código que a CPU realmente vai executar ??? Além disso, todo o JIT que você faz é tempo perdido em comparação com um programa já compilado.

A Coleta de Lixo é uma ferramenta que simplesmente desaloca recursos que um programador teria esquecido de desalocar, de maneira mais ou menos eficiente.

Evidentemente, isso só pode ser mais lento do que um programador C especialista (você escolheu o termo) faria para lidar com sua memória (e não há vazamentos em aplicativos escritos corretamente).

Um aplicativo C otimizado para desempenho sabe que a CPU está sendo executada, foi compilada, senão isso significa que você não executou todas as etapas de desempenho, não é?

Estatísticas de tempo de execução Isso está além do meu conhecimento, mas suspeito que um especialista em C tenha conhecimento de previsão de ramificação mais do que suficiente para ser mais esperto do que a otimização automatizada -

Bibliotecas muito boas Existem muitas funções não muito otimizadas prontamente disponíveis através de bibliotecas em Java, e o mesmo ocorre em qualquer linguagem, no entanto, as bibliotecas mais otimizadas são escritas em C, especialmente para cálculo.

A JVM é uma camada de abstração, que implica as duas coisas boas, muitas das quais estão acima, e também implica que a solução geral é mais lenta por design.

No geral:

O Java nunca pode atingir a velocidade do C / C ++ devido à maneira como ele trabalha em uma JVM com muita proteção, recursos e ferramentas.

O C ++ tem uma vantagem clara em software otimizado, seja para computação ou jogos, e é comum ver implementações em C ++ ganharem concursos de codificação a tal ponto que as melhores implementações em Java só podem ser vistas na segunda página.

Na prática, o C ++ não é um brinquedo e não permitirá que você se engane com muitos erros que a maioria das linguagens modernas pode suportar, no entanto, por ser mais simples e menos seguro, é inerentemente mais rápido.

E, como conclusão, gostaria de dizer que a maioria das pessoas não dá a mínima para isso, que no final a otimização é um esporte reservado apenas a muito poucos desenvolvedores sortudos e que, exceto nos casos em que o desempenho é realmente uma preocupação (ou seja, onde a multiplicação de hardware por 10 não o ajudará - ou representará alguns milhões, pelo menos), a maioria dos gerentes prefere um aplicativo não otimizado e uma tonelada de hardware.

Morg.
fonte
Novamente. A coleta de lixo não é apenas uma "ferramenta que desaloca". O GC pode compactar suas estruturas. O GC pode lidar com suas referências fracas e ajudá-lo a equilibrar seu cache dessa maneira. O GC de vários estágios torna a alocação de heap muito mais barata que a volumosa, lenta newou malloc(). Em geral, pode ser muito mais rápido do que qualquer gerenciamento manual de memória - já que você não seria capaz de realocar objetos manualmente. Portanto, todo o seu raciocínio está claramente errado e tendencioso. Seu conhecimento dos algoritmos de GC e dos métodos de otimização de JIT é muito limitado.
SK-logic,
4
Essa resposta está cheia de conceitos errados sobre o que os otimizadores modernos podem fazer. O código otimizado manualmente não tem chance. Mas então, o C ++ também possui um compilador de otimização.
precisa saber é o seguinte
Obrigado pelo seu comentário SK-logic, mas como você declara, o GC pode ser muito mais rápido em geral, estamos falando sobre o que será o mais rápido em um caso específico, e parece que a maioria das pessoas concorda que qualquer coisa que o GC possa fazer um programador pode fazer, e ainda melhor. Claro que você pode realocar objetos manualmente quando tiver acesso direto à memória .. lol. Meu conhecimento dos internos da JVM é limitado e espero que os chefes de Java me mostrem a luz, não apenas me digam porcaria aleatória sobre o GC ser capaz de fazer coisas que não se pode fazer manualmente (lol ... até o GC precisa use as instruções da CPU;)).
Morg.
Konrad, concordo que subestimo amplamente os otimizadores modernos ... no entanto, acho interessante que você considere o código otimizado à mão como inferior ao código otimizado automaticamente. O que exatamente você espera que o compilador veja que um humano não pode?
Morg.
1
Direito . continue pressionando -1, isso não mudará o fato de que o C ++ é mais rápido que o Java. Talvez eu não saiba muito sobre os compiladores modernos, mas isso não faz nenhuma diferença no ponto principal, o que é correto e contradiz a resposta mais votada aqui. Por que outro motivo o C ++ seria uma prioridade para a nVidia em suas GPUs para HPC. Por que outro motivo todos os jogos seriam escritos em C ++, por que outro mecanismo de banco de dados único seria escrito em C?
Morg.
-4

Eu já vi pelo menos duas mmo impressionantes feitas em Java, para dizer que não é rápido o suficiente para jogos, é um nome impróprio. Só porque os designers de jogos preferem mais o C ++ do que outras linguagens, diz que não se trata apenas de Java, significa que os programadores nunca se interessaram por outras linguagens / paradigmas de programação. Qualquer coisa em qualquer linguagem tão avançada quanto C / C ++ ou mesmo Java pode produzir código que tecnicamente possa atender ou anular o argumento da velocidade. Tudo bem e dito se resume ao que os programadores sabem, com quais equipes as equipes trabalham mais e mais importante porque usam as ferramentas mencionadas. Como estamos abordando o aspecto de desenvolvimento de jogos da programação, deve haver mais no argumento. Basta colocar ' tudo sobre dinheiro e tempo para um negócio morto, que utiliza ferramentas que atendem ao controle de qualidade e, no mundo real, não pesa nos motivos xx para escolher C ++ em Java ou em qualquer outro idioma. É apenas uma decisão de produção em massa. No nível mais básico dos algoritmos de computação, todos com os quais estamos jogando são uns e zeros, o argumento da velocidade é um dos argumentos mais estúpidos já aplicados aos jogos. Se você quer muito ganho de velocidade, abandone completamente as linguagens de programação e trabalhe com o assembly, que é de longe a melhor vantagem.

Meh
fonte
2
Este mural de texto não parece adicionar nada que ainda não tenha sido mencionado nas outras respostas. Por favor edite sua resposta para ser mais legível, e por favor, certifique-se de que os seus endereços de resposta não pontos levantados pela outra resposta. Caso contrário, considere excluir sua resposta, pois ela só adiciona ruído neste momento.