Por que as máquinas virtuais são necessárias?

8

Em vez de compilar o código-fonte para o respectivo SO (no qual está direcionado), você compila uma vez e executa em qualquer lugar.

Para fins de pergunta, eu chamaria VM (por exemplo, para Java e .NET). Portanto, a execução de programas se torna algo como

 ------------       ----      ----
| Executable |  -> | VM | -> | OS |
 ------------       ----      ----

Faz perfeitamente sentido, o compilador permanece genérico para a respectiva VM. No entanto, a implementação da VM pode variar dependendo da máquina que será instalada, ou seja, (* nix, windows, mac) x (32 bits, 64 bits).

Minha pergunta é, em vez de escrever VM para as respectivas máquinas, por que o compilador não foi escrito para essa máquina específica? Por isso, em vez de fazer o download da respectiva VM, você faz o download do respectivo compilador e esse compilador cuidará do código de máquina + SO dessa máquina específica. Resultado final, a execução do código nativo para qualquer máquina. Definitivamente, cada código-fonte precisaria de compilação para essa máquina específica, mas hoje em dia, os sistemas automatizados, scm builds, podem nos ajudar a fazer isso.

Minhas razões para estar confuso estão corretas ou estou perdendo alguns detalhes técnicos aqui?

Editar:

PORTABILIDADE :

Sim, é um dos motivos, mas a portabilidade é um grande problema nos sistemas automatizados atuais? com que frequência precisamos nos preocupar com o fato de que não precisamos compilá-lo para outras máquinas? Ter um código compilado para uma máquina nativa daria um desempenho muito melhor. Pegue o Java, por exemplo, você não pode fazer programação de baixo nível no Windows e precisa escolher JNI.

Tome sistemas automatizados como TeamCity / Jenkins ou outros. Poderíamos ter uma configuração de sistema automatizada em que o código enviado pelo controle de versão resultasse no executável.

Em Ae
fonte
5
você respondeu a sua própria pergunta no primeiro parágrafo
catraca aberração
6
não era novidade no Java. O UCSD Pascal (por apenas um exemplo) fez o mesmo.
Jerry Coffin
2
@EmAe E gostaria de salientar a ironia de você ter escolhido dois sistemas de criação de CI que são executados na JVM.
Andrew T Finnell
1
"em vez de fazer o download da respectiva VM, você faz o download do respectivo compilador e esse compilador cuidará do código da máquina + do SO dessa máquina específica" - temos isso, chamado C. É a essência de como o software é distribuído no Linux, por exemplo (antes dos gerenciadores de pacotes). Sinto muito, mas tenho que votar para fechar.
GrandmasterB
Todo compilador possui apenas uma sequência de máquinas virtuais. Você pode parar a compilação em algum momento, serializar essa representação intermediária e chamar o restante do compilador de "intérprete de máquina virtual". Não vai mudar nada.
SK-logic

Respostas:

16

Minha pergunta é, em vez de escrever VM para as respectivas máquinas, por que o compilador não foi escrito para essa máquina específica?

Porque então você não teria mais um executável portátil; você teria um executável para cada plataforma. O usuário precisaria baixar um executável específico para sua plataforma ou compilar o código em sua plataforma específica.

Com uma VM, cada plataforma precisa apenas de uma VM específica da plataforma e, em seguida, você pode distribuir o mesmo executável para todas as plataformas.

Robert Harvey
fonte
2
As VMs de hoje têm melhor desempenho do que você acredita. O uso de uma VM fornece todos os itens gerenciados, como um sistema de tipos, uma estrutura de biblioteca e uma coleta de lixo de graça.
Robert Harvey
3
@ Robert: Lembre-se, nem todo código nativo é C ou C ++. Eles não demoram a escrever programas porque são nativos e, portanto, "lidam com muitos meandros de baixo nível"; eles demoram a escrever programas porque são linguagens mal projetadas que não fazem um bom trabalho ao lidar com coisas de baixo nível. Mas você pode obter o desempenho de nível C do Delphi com a mesma facilidade de desenvolvimento encontrada nas linguagens gerenciadas.
Mason Wheeler
7
@ Andrew: As pessoas podem dizer que o Java é rápido, de acordo com algumas referências que testam algum algoritmo em alguns casos isolados. Mas então você executa um programa Java real, como o OpenOffice, e leva 30 segundos para abrir a caixa de diálogo Salvar, comparada a cerca de 1-2 segundos no código nativo, e eles concluem que toda essa velocidade teórica no benchmarks não significa nada; Java ainda está lento.
Mason Wheeler
2
@MasonWheeler Como você excluiu seu comentário e expandiu para um novo. A abundância de pessoas que podem se safar da "programação" na linguagem Java é significativamente maior porque a barreira de entrada para C e C ++ é proibitiva, portanto, programas realmente ruins. Java não é lento e as evidências anedóticas de um horrível conjunto de aplicativos não provam o contrário.
Andrew T Finnell
10
@MasonWheeler: Apenas para esclarecer um equívoco comum: o OpenOffice não está escrito em Java - é principalmente em C ++. Ele só precisa do Java para algumas funções especiais. Consulte wiki.services.openoffice.org/wiki/Java_and_OpenOffice.org .
fácil
12

Você está perdendo algo enorme com essas VMs. Eles fazem exatamente o que você diz, mas automaticamente. É chamado de Compilador Just-In-Time e é por isso que o .NET (No Windows) e o Java estão extremamente próximos da velocidade do código C ++ compilado nativamente.

O código-fonte Java / C # é convertido em código de bytes. Esse código de byte é então compilado no código da máquina na qual está sendo executado no momento. Na maior parte, a VM executará o código nativo em vez de reprocessar o código de bytes.

Eu pulei um pouco e simplifiquei demais o processo, mas as VMs fazem uma quantidade substancial de trabalho.

Andrew T Finnell
fonte
Você não está recebendo minha pergunta. Não tenho dúvidas sobre o desempenho do código quando ele está sendo executado na VM específica. A questão é por que usar a VM quando podemos ter um compilador para compilá-lo para uma máquina específica.
Em Ae
2
@EmAe A VM o compila para a máquina específica. É chamado de JIT. Eu sugiro que você leia sobre isso.
Andrew T Finnell
1
Ok, estou confuso aqui. Me corrija e me ajude. Então o compilador compila para uma máquina específica, mesmo assim ele é escolhido para ser executado na VM? Se esse código compilado (digamos JAVA, por exemplo) for portado para uma VM no UNIX / Mac / Ubuntu, isso exige que seja recompilado? NÃO. Como para a VM, o nativo é BYTE-CODE e esse BYTE-CODE é convertido usando JIT. Program -> Compiled to byte code -> JIT for machine -> Execution. 4 etapas estão envolvidas. Estou perguntando por que estamos usando essas 4 etapas? então nós podemosProgram -> Machine specific compiler -> Execute
Em Ae
O mesmo motivo para você não reescrever todo o seu código repetidamente no assembly. Reuso.
Andrew T Finnell
2
Além disso, ninguém diz que você precisa executar essas quatro etapas; você sempre pode usar um compilador AOT. gcc.gnu.org/java
TomJ:
7

Como muitos conceitos em ciência da computação, a VM fornece uma camada de abstração. Você escreve seu código em uma 'interface' (código de bytes / idioma intermediário) e a camada de abstração (a VM) lida com os detalhes da implementação (compilando-o para a máquina de destino e outras tarefas). A camada de abstração fornece um conjunto de serviços cujos detalhes de implementação você não precisa mais se preocupar. Nesse caso, você não precisa mais se preocupar com as especificidades do hardware subjacente, uma vez que os serviços da camada de abstração (VM) estão presentes. O cliente se beneficia de maneira semelhante - ele não precisa conhecer os detalhes sobre sua plataforma, apenas para poder usar a camada de abstração.

Obviamente, existem trocas com qualquer abstração. Você perde o controle refinado sobre os detalhes do outro lado da abstração e precisa confiar nessa abstração para empregar uma implementação sensata. Você precisa considerar os ganhos potenciais por meio do uso de uma abstração em relação às compensações. Em certas aplicações, os inconvenientes podem pesar os benefícios - você pode precisar desse alto nível de controle para cada plataforma.

Sua proposta de usar um sistema automatizado para compilações em diferentes plataformas é exatamente o que as camadas de abstração .NET e Java já fazem. Um dos benefícios da compilação JIT é que o compilador possui detalhes específicos sobre a máquina na qual está executando. Isso pode introduzir potenciais otimizações que, de outra forma, não seriam possíveis ao executar uma compilação de versão na máquina de um desenvolvedor.

Se você estiver preocupado com a desaceleração do tempo de execução devido à geração do código nativo e à execução do programa juntos, você tem a opção de gerar o código nativo durante a instalação e não quando o programa é executado pela primeira vez. O .NET fornece nGen para essa finalidade, que pode executar a geração de código nativo no momento da instalação, em vez do tempo de execução. O CLR utilizará o código nativo em cache em vez de executar a compilação JIT.

medkg15
fonte
3

Você está simplificando muito isso, a plataforma cruzada é mais do que apenas compilar para um conjunto de instruções nativo específico. Mais comum do que não, o mesmo código C / C ++ não pode ser recompilado apenas em plataformas diferentes porque as APIs e as bibliotecas são diferentes. Por exemplo, o desenvolvimento da GUI é muito diferente no Windows e no Ubuntu Linux, a comunicação por soquete provavelmente não é idêntica no Unix e IBM z / OS e assim por diante.

Portanto, para conseguir "escrever uma vez, compilar (e vincular) em qualquer lugar", você precisará criar uma camada de abstração de vários tipos, onde criará uma API comum para todos os serviços no nível do SO. Então você precisa implementar e distribuir isso para todas as plataformas que deseja suportar.

Além disso, embora os modelos de memória sejam cada vez mais semelhantes hoje em dia, ainda existem algumas diferenças entre plataformas diferentes, portanto você também precisa abstrair a alocação de memória. Talvez o mais fácil seja criar seu próprio gerenciador de memória "virtual" sobre o nativo.

Enquanto você está nisso, os sistemas de arquivos e o controle de acesso (ACL) são tratados de maneira bem diferente entre, digamos, o Unix e o Windows, então você precisará abstrair essas coisas também. Talvez até crie seu próprio wrapper "Arquivo" para implementações subjacentes.

Então nós temos rosqueamento. Como o Linux possui apenas processos e o Windows possui threads, precisamos abstrair isso de alguma forma também.

Bem, neste ponto, você praticamente construiu uma máquina virtual. O ponto aqui é que, embora compilar algum código arbitrário para diferentes plataformas seja bastante fácil, criar software funcional de qualquer complexidade que possa ser executada em qualquer plataforma está longe de ser trivial.

pap
fonte
2

Outra grande vantagem da abordagem VM / JIT é a compatibilidade binária. Por exemplo, digamos que você tenha um assembly / JAR / DLL que contenha a classe A e outro assembly que contenha a classe B, que deriva da classe A. O que acontece se você alterar a classe A, por exemplo, você adiciona um membro privado? A adição de um membro privado não deve ter nenhuma influência sobre a classe B, mas se o assembly que contém B já foi compilado no código nativo, você provavelmente obteria bugs estranhos e falhas difíceis de reproduzir, porque o código nativo foi compilado com o antigo layout de memória de A, de modo que, por exemplo, reservaria pouca memória para as variáveis ​​de A, para que os membros de A e os membros B adicionados fossem mapeados para os mesmos locais de memória.

Se você estiver usando uma VM, por outro lado, todos esses problemas simplesmente desaparecerão, porque quando o código é compilado no código nativo, o layout de todas as classes é conhecido.

nikie
fonte
2

Minha pergunta é, em vez de escrever VM para as respectivas máquinas, por que o compilador não foi escrito para essa máquina específica?

Vamos imaginar por um segundo que é assim que funciona. Eu escrevo um aplicativo Java, que eu compilo - o compilador gera um executável para todas as plataformas suportadas. Agora tenho os seguintes executáveis:

  • Solaris x64
  • Solaris x86
  • Solaris SPARC
  • Windows x86
  • Windows x64
  • Linux x86
  • Linux x64
  • Mac OS
  • BSD

etc etc.

Depois, carrego tudo isso no meu site e forneço um link de download para cada um. Em seguida, submeto tudo isso para download.com, tucows.com, sofpedia.com, enviando cada um individualmente.

Isso parece uma grande dor para mim! Quando atualizo o software para a versão 1.1, preciso repetir esse processo.

E o que acontece quando surge uma nova plataforma? Agora, meu software não está disponível para esta plataforma, a menos que eu atualize meu software de desenvolvedor para suportá-lo, recompile e lance novamente meu software para esta nova plataforma.

E o usuário? Eles visitam meu site e precisam selecionar em uma lista exatamente a versão correta do meu software para usar em sua plataforma. A maioria dos usuários não sabe se executam x86 ou x64; portanto, provavelmente acabarão baixando a versão errada e recebendo algum tipo de erro. E se eles mudarem do Windows para um Mac, agora precisam voltar e baixar novamente o meu software.

Tudo isso é um grande problema, tanto para o desenvolvedor quanto para o usuário, e tudo isso pode ser evitado compilando-se em um bytecode e executando em uma VM, exatamente como o Java atualmente faz.

Se o desempenho é quase idêntico à execução de código nativo, a questão não é tanto por que não compilar com código nativo, mas por que se preocupar em compilar com código nativo?

Gavin Coates
fonte
1

Na edição da sua postagem, você questiona a utilidade da portabilidade, por meio de VMs.

Na minha vida profissional, a portabilidade tem sido o fator mais importante - minha plataforma de implantação raramente é a mesma em que implanto e, portanto, sou capaz de desenvolver e testar no Windows, passar para o meu departamento de controle de qualidade, que testaria no Linux e implante em nosso ambiente de produção no Solaris.

Este não é apenas um exemplo isolado e artificial - esse tem sido o pão da minha existência profissional nos últimos 12 anos da minha carreira de várias décadas. Tenho certeza de que existem muitos outros com as mesmas experiências ou com experiências semelhantes.

Se usássemos compiladores (cruzados) diferentes (no mesmo código-fonte) para produzir binários diferentes, você não se sentiria confiante na qualidade de cada binário, se eles não fossem testados em seu próprio SO / arquitetura.

O maravilhoso projeto FreePascal Compiler de código aberto tem uma linha de tag "escreva uma vez, compile em qualquer lugar" - enquanto isso for verdade, eu não esperaria ver nenhuma casa de software respeitável fazendo isso e liberando os aplicativos sem testes completos em todas as plataformas.

Crollster
fonte
Ainda questionaria a validade dos testes em uma plataforma e implantação em outra, mesmo ao usar uma VM. Você está implantando em duas implementações de VM diferentes, uma pode conter um bug específico do sistema que não está presente na outra. As chances são pequenas, mas ainda é uma boa prática testar e implantar em sistemas idênticos.
precisa saber é o seguinte
Na verdade, eu concordo, mas é mais fácil testar uma VM do que um compilador? (ou seja, uma VM é mais confiável, pois é mais fácil de testar ou é igualmente arriscada?) Pelo menos com VMs, você sabe que o código foi usado por milhares (milhões?) de outras pessoas, enquanto somente você (e um outros) compilaram seu aplicativo.
Crollster
Crollster: Eu concordo. no mundo real, você usaria hardware e software idênticos, mas isso às vezes não é possível. O uso de uma plataforma diferente pode ser aceitável na maioria dos casos, mas vale lembrar que existe um risco de erro, embora seja relativamente pequeno.
precisa saber é o seguinte
1

Fácil: teste.

Se o código funcionar bem na Máquina Virtual, você pode ter certeza de que será executado em outro hardware que possui uma VM comprovada.

Se você compilar para diferentes plataformas, precisará fazer muitos testes, porque cada plataforma possui suas próprias peculiaridades.

Pieter B
fonte
0

Há mais na portabilidade do que apenas ser capaz de compilar o código!

O comportamento de um programa C pode ser diferente em plataformas fidderentes:

int  i;
char c[6];
int * p;

p = &c[2];
p = p + 1;

Funciona sem reclamação em um chip IBM Power, mas, por uma exceção em um chip SPARC.

As configurações de sistema operacional, versão do sistema operacional e variáveis ​​de ambiente do sistema de arquivos podem afetar a maneira como um programa compilado será executado.

Uma JVM praticamente garante o mesmo comportamento, independentemente do ambiente de hardware e software.

James Anderson
fonte