Idiomas compilados vs. interpretados

284

Estou tentando entender melhor a diferença. Encontrei muitas explicações online, mas elas tendem mais às diferenças abstratas do que às implicações práticas.

A maioria das minhas experiências de programação foi com CPython (dinâmico, interpretado) e Java (estático, compilado). No entanto, entendo que existem outros tipos de linguagens interpretadas e compiladas. Além do fato de que arquivos executáveis ​​podem ser distribuídos a partir de programas escritos em linguagens compiladas, existem vantagens / desvantagens em cada tipo? Muitas vezes, ouço pessoas argumentando que as linguagens interpretadas podem ser usadas interativamente, mas acredito que as linguagens compiladas também podem ter implementações interativas, correto?

chimeracoder
fonte
32
Você escolheu exatamente os piores idiomas para essa comparação. Ambos são compilados. A única diferença real entre eles é o JITer, e até o Python tem uma parcial (psyco).
Ignacio Vazquez-Abrams
1
Um bom exemplo de uma linguagem compilada interativa é o Clojure - tudo é totalmente compilado (primeiro na JVM e depois no código nativo via JIT). No entanto, grande parte da recompilação acontece dinamicamente, e o desenvolvimento geralmente é feito em um shell REPL interativo, onde você pode avaliar qualquer função que desejar no ambiente em execução.
Mikera
ML padrão é outra linguagem compilada interativa; o compilador embutido também emite código de máquina nativo real.
Donal Fellows

Respostas:

459

Uma linguagem compilada é aquela em que o programa, uma vez compilado, é expresso nas instruções da máquina de destino. Por exemplo, uma operação "+" adicional no seu código-fonte pode ser traduzida diretamente para a instrução "ADD" no código de máquina.

Uma linguagem interpretada é aquela em que as instruções não são executadas diretamente pela máquina de destino, mas sim lidas e executadas por algum outro programa (que normalmente é escrito no idioma da máquina nativa). Por exemplo, a mesma operação "+" seria reconhecida pelo intérprete no tempo de execução, que chamaria sua própria função "add (a, b)" com os argumentos apropriados, que executariam a instrução "ADD" do código de máquina .

Você pode fazer tudo o que puder em uma linguagem interpretada em uma linguagem compilada e vice-versa - ambos são Turing completos. No entanto, ambos têm vantagens e desvantagens de implementação e uso.

Vou generalizar completamente (os puristas me perdoem!), Mas, a grosso modo, aqui estão as vantagens das linguagens compiladas:

  • Desempenho mais rápido usando diretamente o código nativo da máquina de destino
  • Oportunidade de aplicar otimizações bastante poderosas durante a fase de compilação

E aqui estão as vantagens das linguagens interpretadas:

  • Mais fácil de implementar (escrever bons compiladores é muito difícil!)
  • Não é necessário executar um estágio de compilação: pode executar o código diretamente "on the fly"
  • Pode ser mais conveniente para idiomas dinâmicos

Observe que técnicas modernas, como a compilação de bytecode, acrescentam alguma complexidade extra - o que acontece aqui é que o compilador tem como alvo uma "máquina virtual" que não é a mesma que o hardware subjacente. Essas instruções da máquina virtual podem ser compiladas novamente posteriormente para obter o código nativo (por exemplo, conforme feito pelo compilador Java JVM JIT).

Mikera
fonte
1
Nem todos os idiomas compilados precisam de um estágio de compilação lento. As implementações Common Lisp sérias são compiladores e geralmente não se importam com um intérprete, preferindo apenas compilar rapidamente rapidamente. Por outro lado, o Java precisa de uma etapa de compilação, e geralmente é visível.
David Thornley
2
@ Kareem: o compilador JIT apenas faz 1) e 2) uma vez - depois disso é o código nativo até o fim. O intérprete precisa fazer ambos 1) e 2) toda vez que o código é chamado (que pode ser muitas, muitas vezes ...). Assim, com o tempo, o compilador JIT vence por uma longa margem.
Mikera
3
Sim, o bytecode é convertido em código de máquina em algum momento durante a execução geral do programa (em oposição a antes da execução do programa, como é o caso de um compilador tradicional). Mas um determinado pedaço de código pode ser executado mais de 10 milhões de vezes durante a execução geral do programa. (Provavelmente) só é compilado uma vez do bytecode ao código da máquina. Portanto, a sobrecarga de tempo de execução do JIT é pequena e pode ser ignorada para programas de longa duração. Depois que o compilador JIT terminar de executar seu trabalho, você estará efetivamente executando o código de máquina puro por todo o caminho.
Mikera #
2
Esta é realmente uma falsa dicotomia. Não há nada intrínseco a uma linguagem que a compile como interpretada. Não passa de um equívoco amplamente aceito. Muitos idiomas têm implementações e todos os idiomas podem ter.
Mmachenry 25/05
2
@mmachenry não é uma dicotomia falsa. "linguagem de programação" inclui design e implementação. Embora no sentido teórico uma determinada definição de linguagem possa ser compilada e interpretada, na prática do mundo real existem diferenças consideráveis ​​na implementação. Ninguém resolveu ainda como efetivamente compilar certas construções de linguagem, por exemplo - é um problema de pesquisa aberto.
Mikera 28/05
99

Uma linguagem em si não é compilada nem interpretada, apenas uma implementação específica de uma linguagem. Java é um exemplo perfeito. Existe uma plataforma baseada em bytecode (a JVM), um compilador nativo (gcj) e um interpeter para um superconjunto de Java (bsh). Então, o que é Java agora? Compilado pelo bytecode, compilado nativamente ou interpretado?

Outros idiomas, que são compilados e interpretados, são Scala, Haskell ou Ocaml. Cada um desses idiomas possui um intérprete interativo, bem como um compilador para código de bytes ou código de máquina nativo.

Portanto, geralmente categorizar idiomas como "compilado" e "interpretado" não faz muito sentido.

lunaryorn
fonte
3
Concordo. Ou, digamos: Existem compiladores nativos (criando código de máquina para a CPU comer) e compiladores não tão nativos (criando itens tokenizados, ou seja, código intermediário, que alguns compiladores just-in-time compilam antes com o código de máquina ( ou durante o tempo de execução ONCE) e existem não-compiladores "reais" que nunca produzem código de máquina e nunca deixam a CPU executar o código. Estes últimos são intérpretes. Hoje, os compiladores nativos que produzem diretamente o código da máquina (CPU) em tempo de compilação estão se tornando cada vez mais raros. Delphi / Codegear é um dos melhores sobreviventes.
TheBlastOne
57

Comece a pensar em termos de a: explosão do passado

Era uma vez, há muito tempo, que vivia na terra dos intérpretes e compiladores de computação. Todo tipo de confusão decorria dos méritos de um sobre o outro. A opinião geral da época era algo parecido com:

  • Intérprete: Rápido de desenvolver (editar e executar). Lento para executar porque cada instrução teve que ser interpretada no código da máquina toda vez que foi executada (pense no que isso significava para um loop executado milhares de vezes).
  • Compilador: lento para desenvolver (editar, compilar, vincular e executar. As etapas de compilação / link podem levar um tempo sério). Rápido de executar. O programa inteiro já estava em código de máquina nativo.

Havia uma ou duas diferenças de ordem de magnitude no desempenho do tempo de execução entre um programa interpretado e um programa compilado. Outros pontos distintivos, a mutabilidade em tempo de execução do código, por exemplo, também foram de algum interesse, mas a principal distinção girou em torno dos problemas de desempenho em tempo de execução.

Hoje, a paisagem evoluiu a tal ponto que a distinção compilada / interpretada é praticamente irrelevante. Muitos idiomas compilados recorrem a serviços de tempo de execução que não são completamente baseados em código de máquina. Além disso, a maioria das linguagens interpretadas é "compilada" em código de bytes antes da execução. Os intérpretes de código de bytes podem ser muito eficientes e rivalizar com algum código gerado pelo compilador do ponto de vista da velocidade de execução.

A diferença clássica é que os compiladores geraram código de máquina nativo, os intérpretes leram o código-fonte e o código de máquina em tempo real usando algum tipo de sistema de tempo de execução. Hoje, restam muito poucos intérpretes clássicos - quase todos compilados em código de bytes (ou algum outro estado semi-compilado), que é executado em uma "máquina" virtual.

NealB
fonte
1
Bom - ótimo resumo no último parágrafo - obrigado!
ckib16
26

Os casos extremos e simples:

  • Um compilador produzirá um executável binário no formato executável nativo da máquina de destino. Este arquivo binário contém todos os recursos necessários, exceto as bibliotecas do sistema; está pronto para ser executado sem preparação e processamento adicionais e funciona como um raio, porque o código é o código nativo da CPU na máquina de destino.

  • Um intérprete apresentará ao usuário um prompt em um loop, onde ele poderá inserir instruções ou códigos e, ao pressionar RUNou equivalente, o intérprete examinará, examinará, analisará e executará interpretativamente cada linha até que o programa seja executado em um ponto de parada ou erro. . Como cada linha é tratada por conta própria e o intérprete não "aprende" nada de ter visto a linha antes, o esforço de converter linguagem legível por humanos em instruções de máquina é incorrido todas as vezes para cada linha, por isso é lento. Pelo lado positivo, o usuário pode inspecionar e interagir com o programa de várias formas: Alterando variáveis, alterando código, executando nos modos de rastreamento ou depuração ... o que for.

Com aqueles fora do caminho, deixe-me explicar que a vida não é mais tão simples. Por exemplo,

  • Muitos intérpretes pré-compilarão o código fornecido, para que a etapa de tradução não precise ser repetida várias vezes.
  • Alguns compiladores compilam não as instruções de máquina específicas da CPU, mas o bytecode, um tipo de código de máquina artificial para uma máquina fictícia. Isso torna o programa compilado um pouco mais portátil, mas requer um interpretador de bytecode em todos os sistemas de destino.
  • Os intérpretes de bytecode (estou vendo Java aqui) recentemente tendem a recompilar o bytecode que obtêm para a CPU da seção de destino pouco antes da execução (chamada JIT). Para economizar tempo, isso geralmente é feito apenas para códigos que são executados com frequência (pontos de acesso).
  • Alguns sistemas que parecem e agem como intérpretes (Clojure, por exemplo) compilam qualquer código que obtêm, imediatamente, mas permitem acesso interativo ao ambiente do programa. Essa é basicamente a conveniência dos intérpretes com a velocidade da compilação binária.
  • Alguns compiladores realmente não compilam, apenas pré-digerem e compactam o código. Ouvi um tempo atrás que é assim que Perl funciona. Às vezes, o compilador está apenas fazendo um pouco do trabalho e a maior parte ainda é interpretação.

No final, hoje em dia, interpretar versus compilar é uma troca, com o tempo gasto (uma vez) na compilação sendo frequentemente recompensado por um melhor desempenho em tempo de execução, mas um ambiente interpretativo que oferece mais oportunidades de interação. Compilar versus interpretar é principalmente uma questão de como o trabalho de "entender" o programa é dividido entre diferentes processos, e a linha está um pouco embaçada atualmente, pois idiomas e produtos tentam oferecer o melhor dos dois mundos.

Carl Smotricz
fonte
23

Em http://www.quora.com/What-is-the-difference-between-compiled-and-interpreted-programming-languages

Não há diferença, porque “linguagem de programação compilada” e “linguagem de programação interpretada” não são conceitos significativos. Qualquer linguagem de programação, e eu realmente quero dizer alguma, pode ser interpretada ou compilada. Assim, interpretação e compilação são técnicas de implementação, não atributos de linguagens.

Interpretação é uma técnica pela qual outro programa, o intérprete, executa operações em nome do programa que está sendo interpretado para executá-lo. Se você pode imaginar lendo um programa e fazendo o que ele diz para fazer passo a passo, digamos em um pedaço de papel de rascunho, é exatamente isso que um intérprete também faz. Um motivo comum para interpretar um programa é que os intérpretes são relativamente fáceis de escrever. Outro motivo é que um intérprete pode monitorar o que um programa tenta fazer enquanto é executado, para impor uma política, por exemplo, por segurança.

Compilação é uma técnica pela qual um programa escrito em um idioma (o "idioma de origem") é traduzido para um programa em outro idioma (o "idioma do objeto"), o que, esperançosamente, significa a mesma coisa que o programa original. Ao fazer a tradução, é comum que o compilador tente também transformar o programa de maneira a acelerar o programa do objeto (sem alterar seu significado!). Um motivo comum para compilar um programa é que há uma boa maneira de executar programas no idioma do objeto rapidamente e sem a sobrecarga de interpretar o idioma de origem ao longo do caminho.

Você deve ter adivinhado, com base nas definições acima, que essas duas técnicas de implementação não são mutuamente exclusivas e podem até ser complementares. Tradicionalmente, a linguagem de objeto de um compilador era código de máquina ou algo semelhante, que se refere a qualquer número de linguagens de programação entendidas por CPUs de computador específicas. O código da máquina seria executado “on the metal” (embora se possa ver, se alguém olhar de perto o suficiente, que o “metal” funcione muito como um intérprete). Hoje, no entanto, é muito comum usar um compilador para gerar código de objeto que deve ser interpretado - por exemplo, é assim que o Java costumava (e às vezes ainda funciona). Existem compiladores que traduzem outros idiomas para JavaScript, que geralmente são executados em um navegador da web, que pode interpretar o JavaScript, ou compile uma máquina virtual ou código nativo. Também temos intérpretes para código de máquina, que podem ser usados ​​para emular um tipo de hardware em outro. Ou então, pode-se usar um compilador para gerar código de objeto que é o código fonte de outro compilador, que pode até compilar código na memória a tempo de ser executado, o que por sua vez. . . Você entendeu a ideia. Existem muitas maneiras de combinar esses conceitos.

Bhavin Shah
fonte
Você pode corrigir esta frase: "Existem compiladores que traduzem outros idiomas para JavaScript, que geralmente são executados em um navegador da web que pode interpretar o JavaScript ou compilá-lo como uma máquina virtual ou código nativo".
precisa saber é o seguinte
Acertou em cheio. Outro erro comum é atribuir a utilidade de um idioma às suas APIs existentes.
Little Endian
10

A maior vantagem do código fonte interpretado sobre o código fonte compilado é a PORTABILITY .

Se o seu código-fonte for compilado, você precisará compilar um executável diferente para cada tipo de processador e / ou plataforma em que deseja que seu programa seja executado (por exemplo, um para Windows x86, um para Windows x64, um para Linux x64, etc.) em). Além disso, a menos que seu código seja totalmente compatível com os padrões e não use nenhuma biblioteca / função específica da plataforma, você precisará escrever e manter várias bases de código!

Se o seu código fonte é interpretado, você só precisa escrevê-lo uma vez e pode ser interpretado e executado por um intérprete apropriado em qualquer plataforma! É portátil ! Observe que o próprio intérprete é um programa executável que é escrito e compilado para uma plataforma específica.

Uma vantagem do código compilado é que ele oculta o código-fonte do usuário final (que pode ser propriedade intelectual ) porque, em vez de implantar o código-fonte legível por humanos original, você implanta um arquivo executável binário obscuro.

Niko Bellic
fonte
1
nesses termos, o java não pode ser considerado uma "linguagem compilada", mas sua fase de compilação oferece as vantagens de compilar (verificação de tipo, detecção precoce de erros etc.) e produz um código de código que pode ser executado em todos os sistemas operacionais, com Java máquina virtual fornecida.
Rogelio Triviño 17/01
7

Um compilador e um intérprete fazem o mesmo trabalho: traduzir uma linguagem de programação para outra linguagem de programação, geralmente mais próxima do hardware, geralmente código de máquina executável direta.

Tradicionalmente, "compilado" significa que essa tradução acontece de uma só vez, é feita por um desenvolvedor e o executável resultante é distribuído aos usuários. Exemplo puro: C ++. A compilação geralmente leva muito tempo e tenta fazer muitas otimizações caras para que o executável resultante seja executado mais rapidamente. Os usuários finais não têm as ferramentas e os conhecimentos necessários para compilar coisas, e o executável geralmente precisa ser executado em uma variedade de hardware, portanto você não pode fazer muitas otimizações específicas de hardware. Durante o desenvolvimento, a etapa de compilação separada significa um ciclo de feedback mais longo.

Tradicionalmente, "interpretado" significa que a tradução acontece "on the fly", quando o usuário deseja executar o programa. Exemplo puro: PHP baunilha. Um intérprete ingênuo precisa analisar e traduzir todo código sempre que é executado, o que o torna muito lento. Ele não pode fazer otimizações complexas e caras, porque levariam mais tempo do que o tempo economizado na execução. Mas ele pode usar totalmente os recursos do hardware em que é executado. A falta de uma etapa de compilação separada reduz o tempo de feedback durante o desenvolvimento.

Hoje em dia, porém, "compilado versus interpretado" não é um problema em preto ou branco; existem tons intermediários. Intérpretes ingênuos e simples estão praticamente extintos. Muitos idiomas usam um processo de duas etapas em que o código de alto nível é traduzido para um bytecode independente da plataforma (que é muito mais rápido de interpretar). Então você tem "compiladores just-in-time" que compilam o código no máximo uma vez por execução do programa, às vezes armazenam em cache os resultados e até decidem inteligentemente interpretar o código que raramente é executado, além de fazer otimizações poderosas para códigos que executam muito. Durante o desenvolvimento, os depuradores são capazes de alternar código dentro de um programa em execução, mesmo para linguagens tradicionalmente compiladas.

Michael Borgwardt
fonte
1
No entanto, o modelo de compilação do C ++ é herdado do C e foi projetado sem considerar recursos, como modelos. Esse constrangimento contribui para os longos tempos de compilação do C ++ muito mais do que qualquer outro fator - e o torna um exemplo ruim.
4

Primeiro, um esclarecimento: o Java não é totalmente estático-compilado e vinculado da maneira que o C ++. É compilado no bytecode, que é então interpretado por uma JVM. A JVM pode fazer a compilação just-in-time na linguagem de máquina nativa, mas não precisa fazê-lo.

Mais ao ponto: acho que a interatividade é a principal diferença prática. Como tudo é interpretado, você pode pegar um pequeno trecho de código, analisar e executá-lo no estado atual do ambiente. Portanto, se você já executou o código que inicializou uma variável, teria acesso a essa variável etc. Ele realmente se presta a coisas como o estilo funcional.

A interpretação, no entanto, custa muito, especialmente quando você tem um sistema grande com muitas referências e contexto. Por definição, é um desperdício porque código idêntico pode ter que ser interpretado e otimizado duas vezes (embora a maioria dos tempos de execução tenha algum cache e otimizações para isso). Ainda assim, você paga um custo de tempo de execução e geralmente precisa de um ambiente de tempo de execução. Também é menos provável que você veja otimizações interprocedimentais complexas porque, atualmente, o desempenho delas não é suficientemente interativo.

Portanto, para sistemas grandes que não mudam muito, e para certos idiomas, faz mais sentido pré-compilar e pré-vincular tudo, faça todas as otimizações que você puder fazer. Isso acaba com um tempo de execução muito enxuto, já otimizado para a máquina de destino.

Quanto à geração de executáveis, isso tem pouco a ver com isso, IMHO. Geralmente, você pode criar um executável a partir de uma linguagem compilada. Mas você também pode criar um executável a partir de uma linguagem interpretada, exceto que o intérprete e o tempo de execução já estão incluídos no pacote executável e ocultos a você. Isso significa que você geralmente ainda paga os custos de tempo de execução (embora eu tenha certeza de que, para alguns idiomas, há maneiras de traduzir tudo para um executável em árvore).

Não concordo que todos os idiomas possam ser interativos. Certas linguagens, como C, estão tão ligadas à máquina e a toda a estrutura de links que não tenho certeza de que você possa criar uma versão interativa significativa e completa.

Uri
fonte
C não está realmente ligado a uma "máquina". A sintaxe e a semântica de C são bastante simples. Não deve ser particularmente difícil implementar um interpretador C, apenas muito demorado (porque a biblioteca padrão também precisa ser implementada). Além disso, o Java pode ser compilado no código de máquina nativo (usando gcj).
lunaryorn
@ lunaryorn: Não concordo com o GCJ. A GCJ apenas fornece um ambiente baseado em executável. "Aplicações compiladas estão ligados com o tempo de execução GCJ, libgcj, que fornece as bibliotecas de classes núcleo, um colector de lixo, e um intérprete de código de bytes"
Uri
2
GCJ faz produzir código de máquina nativo, e não apenas um ambiente executável com intérprete embutido e bytecode. A libgcj fornece um interpretador de bytecode para suportar chamadas do código nativo para o bytecode Java, não para interpretar o programa compilado. Se a libgcj não fornecesse um intérprete de bytecode, o GCJ não obedeceria à especificação Java.
lunaryorn
@lunaryorn: Ah. Ok, eu aprecio o esclarecimento e permaneço corrigido. Usamos principalmente Java em um ambiente Windows, então eu não tentei o gcj há anos.
Uri
2

É bastante difícil dar uma resposta prática, porque a diferença está na própria definição de linguagem. É possível criar um intérprete para cada idioma compilado, mas não é possível criar um compilador para cada idioma interpretado. É muito sobre a definição formal de uma linguagem. Para que a informática teórica coisas que ninguém gosta na universidade.

Steven Mohr
fonte
1
Certamente você pode construir um compilador para uma linguagem interpretada, mas o código de máquina compilado é, ele próprio, um espelho do tempo de execução.
Aiden Sino
2

O Python Book © 2015 Imagine Publishing Ltd, simplesmente distingue a diferença pela seguinte dica mencionada na página 10 como:

Uma linguagem interpretada como Python é aquela em que o código-fonte é convertido em código de máquina e, em seguida, executado sempre que o programa é executado. Isso é diferente de uma linguagem compilada como C, onde o código-fonte é convertido em código de máquina apenas uma vez - o código de máquina resultante é então executado toda vez que o programa é executado.

Ahmed Shaban Helwa
fonte
1

Compilar é o processo de criação de um programa executável a partir de código escrito em uma linguagem de programação compilada. A compilação permite que o computador execute e entenda o programa sem a necessidade do software de programação usado para criá-lo. Quando um programa é compilado, ele geralmente é compilado para uma plataforma específica (por exemplo, plataforma IBM) que funciona com computadores compatíveis com IBM, mas não com outras plataformas (por exemplo, plataforma Apple). O primeiro compilador foi desenvolvido por Grace Hopper enquanto trabalhava no computador Harvard Mark I. Hoje, a maioria das linguagens de alto nível inclui seu próprio compilador ou possui kits de ferramentas disponíveis que podem ser usados ​​para compilar o programa. Um bom exemplo de um compilador usado com Java é o Eclipse e um exemplo de um compilador usado com C e C ++ é o comando gcc.

salehvm
fonte
0

Definição curta (não precisa):

Idioma compilado: o programa inteiro é traduzido para o código da máquina de uma só vez e, em seguida, o código da máquina é executado pela CPU.

Linguagem interpretada: o programa é lido linha por linha e, assim que uma linha é lida, as instruções da máquina para essa linha são executadas pela CPU.

Mas, na verdade, poucos idiomas hoje em dia são puramente compilados ou puramente interpretados, geralmente é uma mistura. Para uma descrição mais detalhada com fotos, consulte este tópico:

Qual é a diferença entre compilação e interpretação?

Ou meu post posterior:

https://orangejuiceliberationfront.com/the-difference-between-compiler-and-interpreter/

uliwitness
fonte