Pergunta

OK, primeiro, não quero nenhum tipo de guerra violenta aqui ou algo parecido.Minha pergunta maior é mais teórica e incluirá alguns exemplos.

Então, como escrevi, não consigo entender como a linguagem interpretada pode ser ainda pouco eficiente.E por ser moderno, tomarei Java como exemplo.

Vamos voltar aos dias em que não existiam compiladores JIT.Java tem sua máquina virtual que é basicamente seu hardware.Você escreve o código e depois o compila em bytecode para retirar pelo menos algum trabalho da máquina virtual, tudo bem.Mas considerando o quão complexo até mesmo o conjunto de instruções RISC pode ser em hardware, não consigo nem pensar em uma maneira de fazê-lo em hardware emulado por software.

Não tenho experiência em escrever máquinas virtuais, então não sei como isso é feito no nível mais eficiente, mas não consigo pensar em nada mais eficiente do que testar cada instrução para correspondência e executar as ações apropriadas.Você sabe, algo como: if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) }etc....

Mas isso deve ser terrivelmente lento.E ainda assim, mesmo que existam artigos que dizem que o Java era lento antes dos compiladores JIT, eles ainda dizem que não é tão lento.Mas para emular, são necessários muitos ciclos de clock de HW real para executar uma instrução de bytecode.

E ainda assim, até plataformas inteiras são baseadas em java.Por exemplo, Android.E as primeiras versões do Android não tinham compilador JIT.Eles foram interpretados.Mas o Android não deveria ser terrivelmente lento?E ainda assim não é.Eu sei, quando você chama alguma função da API, da biblioteca do Android, elas são escritas em código de máquina, então são eficientes, então isso ajuda muito.

Mas imagine que você escreveria seu próprio mecanismo de jogo do zero, usando API apenas para exibir imagens.Você precisaria fazer muitas operações de cópia de array, muitos cálculos que seriam terrivelmente lentos quando emulados.

E agora alguns exemplos como prometi.Como trabalho principalmente com MCUs, encontrei JVM para Atmel AVR MCU.Thay afirma que o MCU de 8 MHz pode executar 20 mil optcodes Java por segundo.Mas como o AVR pode executar a maioria das instruções em um ou dois ciclos, digamos uma média de 6.000.000 instruções.Isso nos dá que a JVM sem o compilador JIT é 300 vezes mais lenta para o código de máquina.Então, por que tornar o Java tão popular sem o compilador JIT?Isso não é uma perda de desempenho muito ruim?Eu simplesmente não consigo entender isso.Obrigado.

Foi útil?

Solução

Tivemos código de bytes há muito tempo. Na antiga Apple II, o sistema P USCD era muito popular, que compilou Pascal no código de bytes, que seria interpretado por um 6502 de 8 bits que pode estar em 2 MHz. Esses programas foram razoavelmente rápidos.

Um intérprete de bytecode geralmente seria baseado em uma mesa de salto em vez de uma corrente de if/then/else declarações. Em C ou C ++, isso envolveria um switch declaração. Fundamentalmente, o intérprete teria o equivalente a uma matriz de código de processamento e usaria o código OPCID na instrução de código de byte como o índice da matriz.

Também é possível ter código de bytes mais alto que as instruções da máquina, para que uma instrução de código de byte se traduza em várias, às vezes numerosas instruções de código da máquina. Um código de bytes que foi construído para um idioma específico pode fazer isso com bastante facilidade, pois só precisa corresponder às estruturas de controle e dados desse idioma em particular. Isso estende a sobrecarga da interpretação e torna o intérprete mais eficiente.

É provável que uma linguagem interpretada tenha alguma penalidade de velocidade quando comparada a uma linguagem compilada, mas isso geralmente não é importante. Muitos programas processam a entrada e saída em velocidade humana, e isso deixa uma quantidade enorme de desempenho que pode ser desperdiçada. Mesmo um programa ligado à rede provavelmente terá muito mais energia da CPU disponível do que precisa. Existem programas que podem usar toda a eficiência da CPU que podem obter e, por razões óbvias, eles tendem a não ser escritos em idiomas interpretados.

E, é claro, há a questão do que você recebe para alguma ineficiência que pode ou não fazer a diferença. As implementações de idiomas interpretadas tendem a ser mais fáceis de transportar do que as implementações compiladas, e o código de byte real é frequentemente portátil. Pode ser mais fácil colocar funcionalidade de nível superior no idioma. Ele permite que a etapa de compilação seja muito mais curta, o que significa que a execução pode começar muito mais rapidamente. Pode permitir um melhor diagnóstico se algo der errado.

Outras dicas

Mas então não deveria ser o Android terrivelmente lento?

Defina "terrivelmente lento". É um telefone. Ele precisa processar "Dial First Digit" antes de discar o segundo dígito.

Em qualquer aplicação interativa, o fator limitante é sempre o tempo de reação humana. Pode ser 100 tempo mais lento e ainda ser mais rápido que o usuário.

Portanto, para responder à pergunta, sim, os intérpretes são lentos, mas geralmente são rápidos o suficiente, principalmente à medida que o hardware continua ficando mais rápido.

Lembre-se de que quando o Java foi introduzido, foi vendido como uma linguagem de applet da web (substituindo e agora substituída por JavaScript-que também interpretou). Foi somente após a compilação do JIT que se tornou popular em servidores.

Os intérpretes de bytecode podem ser mais rápidos que uma linha de if () s usando uma tabela de salto:

 void (*jmp_tbl)[256] = ...;  /* array of function pointers */
 byte op = *program_counter++;
 jmp_tbl[op]();

Existem duas maneiras diferentes de abordar essa questão.

(i) "Por que está tudo bem executar código lento"

Como James já mencionado acima, às vezes a velocidade de execução não é tudo o que você está interessado. Para muitos aplicativos em execução no modo interpretado, pode ser "rápido o suficiente". Você deve levar em consideração como o código que você está escrevendo será usado.

(ii) "Por que é interpretado o código deficiente"

Existem muitas maneiras de implementar um intérprete. Na sua pergunta, você fala sobre a abordagem mais ingênua: basicamente uma grande mudança, interpretando cada instrução da JVM como é lida.

Mas você pode otimizar isso: por exemplo, em vez de analisar uma única instrução de JVM, você pode olhar para uma sequência deles e procurar padrões para os quais você tem interpretações mais eficientes disponíveis. A JVM da Sun realmente faz algumas dessas otimizações no próprio intérprete. Em um emprego anterior, um cara levou algum tempo para otimizar o intérprete dessa maneira e interpretou Java Bytecode estava executando visivelmente mais rápido após suas mudanças.

Mas nas JVMs modernas que contêm um compilador JIT, o intérprete é apenas um trampolim até que o JIT faça seu trabalho, para que as pessoas não gastem muito tempo otimizando o intérprete.

12 MHz seria um ATtiny, que é um microprocessador de 8 bits.Isso significa (por exemplo) que uma instrução nativa 'Adicionar' só pode adicionar dois números de 8 bits para obter um resultado de 9 bits.A JVM é basicamente um processador virtual de 32 bits.Isso significa que sua instrução add adiciona dois números de 32 bits para produzir um resultado de 33 bits.

Assim, ao comparar taxas de instrução, você deve esperar uma redução de 4:1 na taxa de instrução como mínimo absoluto.Na realidade, embora seja fácil simular uma adição de 32 bits com 4 adições de 8 bits (com carregamentos), algumas coisas não são escalonadas assim.Apenas por exemplo, de acordo com o próprio Atmel nota do aplicativo, uma multiplicação 16x16 produzindo um resultado de 32 bits é executada em aproximadamente 218 ciclos de clock.A mesma nota do aplicativo mostra uma divisão de 16/16 bits (produzindo um resultado de 8 bits) em execução em 255 ciclos.

Assumindo essa escala linearmente, podemos esperar que as versões de 32 bits da multiplicação levem entre 425 e 450 ciclos de clock e a divisão entre cerca de 510 ciclos.Na realidade, provavelmente deveríamos esperar um pouco de sobrecarga, o que reduziria ainda mais a velocidade – adicionar pelo menos 10% a essas estimativas provavelmente as tornaria mais realistas.

Conclusão:quando você compara maçãs com maçãs, fica aparente que grande parte da diferença de velocidade que você está falando não é real (ou não é uma sobrecarga de JVM atribuível de qualquer maneira).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top