Uploaded avatar of iHiD

É o Novembro Nibble

@iHiD
Quase 3 anos atrás
Vídeo

Introdução

Oi, pessoal. Bem-vindos a novembro. Espero que vocês estejam bem.

Tivemos um outubro superagitado. Acabamos de lançar uma grande melhoria nas soluções da comunidade. Agora removemos as duplicatas, então as soluções parecidas aparecem só uma vez, adicionamos novas opções de ordenação, a possibilidade de buscar por código e, se você olhar C#, vai ver que também adicionamos a possibilidade de filtrar por diferentes conceitos de programação. Então você pode procurar soluções que usam deslocamento de bits, recursão ou qualquer outra coisa que quiser. Vamos levar isso para outras trilhas na semana da comunidade.

Mas, por enquanto, vamos nos concentrar no #12in23. Outubro foi um mês interessante, explorando linguagens orientadas a objetos, mas neste mês vamos com tudo. Vamos nos concentrar em linguagens Assembly e, especificamente, em Assembly MIPS, Assembly x86-64 e WebAssembly. Como sempre, o Erik vai nos explicar o que torna essas linguagens interessantes e únicas.

Os emblemas

Como sempre, você pode ganhar o emblema do Novembro Nibbly completando 5 exercícios quaisquer nessas linguagens. Também temos o emblema do Ano Inteiro, que sei que muitos de vocês estão buscando. Para isso, temos 5 exercícios em destaque para você completar. São eles:

  • Contagem de bits: contar os bits 1 em um número
  • Grãos: calcular o número de grãos em um tabuleiro de xadrez com casas que dobram
  • Cor do resistor: converter a cor de uma faixa de resistor na sua representação numérica
  • Cifra rotacional: implementar a cifra rotacional (também conhecida como cifra de César)
  • Contagem de nucleotídeos: calcular quantas vezes cada nucleotídeo aparece em uma string de DNA

Contexto

Por que o nome Novembro Nibble?

Bem, como você provavelmente sabe, um byte tem 8 bits. E um nibble tem 4 bits. O nome começa a fazer sentido se você trocar o y de byte por um i, o que resulta em bite. Um nibble é, então, uma pequena mordida.

O que é uma linguagem assembly?

Bem, vamos começar pelo básico. Uma CPU executa instruções, como "somar dois números" ou "deslocar bits à esquerda", etc. Essas instruções são conhecidas como instruções de código de máquina e não passam de sequências específicas de bits. Executar um programa é, "simplesmente" (entre aspas), a CPU processando e executando essas sequências de bits.

Como escrever instruções diretamente como sequências de bits é trabalhoso e sujeito a erros, Kathleen e Andrew Donald Booth criaram, lá em 1947, uma linguagem mais amigável para representar instruções de código de máquina. Uma linguagem assim, que representa instruções de código de máquina, é chamada de linguagem assembly. Essa linguagem assembly é então convertida em instruções de código de máquina por meio de um "montador".

Um aspecto interessante das linguagens assembly é que elas estão diretamente ligadas a uma arquitetura de CPU, embora normalmente sejam independentes do sistema operacional.

A propósito, se você lembra dos cartões perfurados, aqueles pedaços grandes de cartão usados nos primeiros computadores para fazê-los executar programas, eles também eram uma linguagem assembly!

Em que a linguagem assembly é diferente das linguagens em que programamos hoje?

A principal diferença é que as linguagens assembly são de nível muito baixo. Você vai sentir falta de muitas abstrações com que já se acostumou. Classes ou objetos não existem em lugar nenhum. Laços? Você vai ter que escrevê-los manualmente usando saltos. Funções? Nada disso! Achei que escrever código assembly dá uma boa lição de humildade, porque aí você percebe o quanto as linguagens modernas facilitam sua vida. Mas também é incrivelmente útil escrever código assembly, porque você vai entender muito melhor como as coisas realmente funcionam.

Curiosidade: 99% do código-fonte de RollerCoaster Tycoon foi escrito à mão em código assembly! É uma conquista impressionante, que você vai passar a admirar ainda mais depois de fazer um pouco de assembly.

As pessoas ainda escrevem em código assembly?

Bem, menos do que costumavam. Antigamente, o assembly feito à mão costumava superar o código de máquina gerado pelo compilador (o que é uma das razões pelas quais a linguagem C++ permite incorporar código assembly diretamente), mas os compiladores ficaram tão bons em gerar código de máquina que isso raramente é verdade hoje. Dito isso, você ainda vai encontrar linguagens assembly sendo usadas em ambientes críticos de desempenho ou com recursos limitados.

Panorama

MIPS

  • MIPS (Microprocessor without Interlocked Pipelined Stages) é uma família de arquiteturas de conjunto de instruções de computador com conjunto reduzido de instruções (RISC)
  • Desenvolvida pela MIPS Computer Systems e lançada pela primeira vez em 1985
  • Várias versões: MIPS I, II, III, IV, V e MIPS32/64. As duas primeiras versões eram apenas de 32 bits, mas o MIPS III introduziu suporte a 64 bits.
  • Várias extensões opcionais, como instruções SIMD e compressão
  • Muito influente nas arquiteturas RISC posteriores
  • A MIPS anunciou em 2021 que a arquitetura MIPS não estava mais sendo desenvolvida e migrou para o RISC-V (que é uma arquitetura de código aberto e livre de royalties)
  • Usada principalmente em sistemas embarcados (por exemplo, roteadores) e servidores (os computadores da Silicon Graphics a usavam, e ficaram famosos por serem usados em efeitos especiais de filmes), no supercomputador NEC Cenju-4, no carro Model S da Tesla, na sonda New Horizons da NASA, mas também para ensinar assembly em universidades e em vários consoles de videogame (por exemplo, o PlayStation original, o PlayStation Portable e o Nintendo 64)

Assembly x86-64

  • Projetado pela AMD e lançado em 1999 como a arquitetura AMD64
  • É uma versão de 64 bits do conjunto de instruções x86, que remonta a 1978, quando a Intel lançou seu microprocessador 8086. Aquele era um processador de 16 bits, mas depois o 80386 adicionou instruções de 32 bits, e esse conjunto de instruções se tornou sinônimo de x86.
  • O principal que os 64 bits permitiram foi endereçar mais memória (o endereçamento de 32 bits é limitado a 4GB), o que tinha se tornado um gargalo. Os 64 bits podem, em teoria, endereçar 16 exabytes, mas atualmente só 48 bits são usados, o que permite endereçar 256 TB (pode ser estendido depois, quando for preciso)
  • O AMD64 estende o conjunto de instruções x86 e foi projetado para ser totalmente compatível com aplicativos existentes de 16 e 32 bits por meio de um modo de compatibilidade
    • A Intel havia projetado o IA-64 sem o envolvimento da AMD. Era um conjunto de instruções de 64 bits novo, muito diferente e incompatível com versões anteriores. No fim, o AMD64 venceu e a Intel implementou sua própria versão dele (com apenas pequenas diferenças semânticas)
  • Usado em toda parte. De estações de trabalho a servidores (incluindo supercomputadores), de sistemas embarcados a consoles de videogame (por exemplo, PS5 e Xbox Series X).

WebAssembly

  • Projetado pelo W3C, um órgão de padronização de tecnologias web
  • Os objetivos de design são:
    • Rápido, seguro e portátil
    • Representação eficiente e portátil
  • Antigamente, a execução rápida na web geralmente era obtida por meio de plugins especializados de navegador, como Flash e Silverlight, já que o próprio JavaScript não se presta muito bem à computação de alto desempenho. As principais desvantagens desses plugins eram que eles normalmente tinham muitos problemas de segurança e não eram padronizados.
    • A Mozilla criou o asm.js, um subconjunto de JavaScript que tinha como objetivo permitir a execução de código no navegador com ótimo desempenho, o que era alcançado por meio de consistência de tipos (sem mudança dinâmica de tipos) e sem coleta de lixo. As linguagens podiam então compilar para asm.js e ainda assim ter bom desempenho ao rodar na web. Mas ainda era JS, então limitado no que podia ser alcançado. Daí a proposta de uma nova linguagem: WASM.
  • Uma linguagem semelhante ao assembly, no sentido de que fornece um conjunto de instruções a serem executadas. Crucialmente, ela não está ligada a uma CPU específica, então é independente de plataforma e exige uma implementação para cada plataforma (uma máquina virtual). Isso significa que o WebAssembly é, na verdade, bytecode, e não código de máquina
  • Tipagem estática (diferença crucial em relação ao JS)
  • Normalmente usa compilação ahead-of-time ou just-in-time (mas pode ser interpretado)
  • Padrão aberto e define duas coisas:
    • Um formato binário
    • Um formato de texto (que é compilado para o formato binário)
  • Implementações em todos os principais navegadores
  • Usado em muitas páginas web que exigem alto desempenho, como Google Earth, Figma, Unity e Autocad. Também vem ganhando espaço no lado do servidor, por exemplo, para rodar microsserviços, rodar em plataformas SaaS (como os workers da CloudFlare) ou no Docker

E, de uma perspectiva de programação, como elas se diferenciam?

MIPS

  • Usa uma arquitetura load-store (também conhecida como registrador-registrador), em que as instruções ou fazem acesso à memória, ou fazem aritmética, mas operam exclusivamente sobre dados nos registradores

Assembly x86-64

  • Usa uma arquitetura registrador-memória, que permite realizar operações na memória (ou a partir dela), assim como nos registradores

WebAssembly

  • Usa programação baseada em pilha (sem registradores), com a opção de ler e gravar dados na memória

O que torna essas linguagens ótimas?

MIPS

  • Pequena. O conjunto de instruções MIPS consegue caber todas as suas instruções em uma única página
  • Convenções de chamada bem estabelecidas ajudam a identificar como usar os registradores disponíveis, por exemplo, quais usar para passar argumentos e quais para retornar resultados.
  • Estável. A última versão foi lançada em 2014
  • Amplamente documentada, especialmente em textos acadêmicos
  • Muito uso no mundo real. Bilhões de dispositivos

Assembly x86-64

  • Apesar de ser uma extensão do x64, muitas funcionalidades novas foram adicionadas, incluindo:
    • Suporte a números inteiros de 64 bits
    • Registradores adicionais
    • Instruções SSE (instruções vetoriais)
    • Acesso relativo a dados (mais eficiente ao usar bibliotecas compartilhadas)
    • Bit No-Execute (recurso de segurança para impedir a execução de código em certas páginas de memória)
  • Familiar. Como estende o conjunto de instruções x86, será relativamente fácil de aprender para quem já conhece o conjunto de instruções x86 Documentação extensa e detalhada
  • Estável. Embora novas versões sejam adicionadas regularmente, o núcleo continua extremamente estável e compatível com versões anteriores

WebAssembly

  • A máquina virtual WebAssembly baseada em pilha é enxuta e simples em comparação com as linguagens assembly dos processadores físicos (incluindo RISC). Isso torna relativamente fácil compilar para ela.
  • O WebAssembly Text Format usa "açúcar" de S-Expression para alcançar um estilo imperativo familiar, que é transformado em código baseado em pilha. A forma com S-Expressions é chamada de "sugared form" e ela "desugars" para a outra forma, equivalente ao que está no binário. As S-expressions serão familiares para quem já trabalhou com LISP
  • Forte interoperabilidade com JavaScript. Simples passar dados de e para JavaScript. Ressalva importante: o WASM ainda não permite interagir com o DOM
  • Melhora continuamente. Não só as máquinas virtuais WASM estão sendo aprimoradas, mas o próprio padrão também está em desenvolvimento ativo. Diversas funcionalidades novas estão sendo projetadas e trabalhadas, como instruções relacionadas a SIMD, coleta de lixo, threads, otimizações de chamada de cauda e mais
  • Segura. O código é validado e executado em um ambiente sandbox, proporcionando um grau maior de validação estática em comparação com JavaScript ou linguagens assembly nativas. A semântica é bem definida, o que facilita verificar e raciocinar sobre o código

Características de destaque

MIPS

  • Eficiência. Os processadores MIPS são muito eficientes, o que os torna ótimos para sistemas embarcados.
  • Desempenho. Ótimo desempenho, tanto que o MIPS foi usado em supercomputadores
  • Fácil de aprender. Com poucas instruções e cada instrução fazendo apenas uma coisa simples, isso a torna fácil de aprender. Ótima para fins didáticos.

Assembly x86-64

  • Poderoso. O x86-64 foi aprimorado ao longo de décadas, com inúmeras instruções que ajudam no desempenho. Um exemplo disso é o SIMD (Single Instruction, Multiple Data), que são instruções que, bem, permitem que uma única instrução seja executada em paralelo sobre vários dados.
  • Onipresente. Dispositivos que rodam x86-64 estão em toda parte. As CPUs da Intel e da AMD o implementam. É o padrão de fato há bastante tempo
  • Atualizado regularmente. Por exemplo, novas instruções vetoriais, com SSE3-5, AVX, AVX-512 e mais

WebAssembly

  • Eficiente. O formato binário é compacto e pode ser decodificado, validado e compilado em uma única passada rápida. Também é streamable, o que permite que a decodificação, a validação e a compilação comecem o quanto antes, antes de todos os dados terem sido vistos. E também é paralelizável. Isso o torna perfeito para aplicações web de alto desempenho.
  • Ótimo alvo de compilação. Permite que código em muitas linguagens rode na web. A maioria das principais linguagens oferece suporte a compilar para binários WebAssembly, o que significa que seu código pode rodar na web sem precisar escrever JavaScript. Algumas linguagens não compilam seu código para WebAssembly, mas sim o runtime, que então pode rodar bytecode sem alterações.
  • Fácil de publicar e rodar. Tudo o que você precisa é de uma máquina virtual que consiga interpretar o bytecode, e todos os principais navegadores incluem uma.
  • Não está preso à web, mas também pode rodar no lado do servidor. A WebAssembly System Interface (WASI) é uma interface (ABI e API) projetada para ser portável para qualquer plataforma. É semelhante ao POSIX (interfaces padrão para sistemas Unix) e fornece coisas como I/O. A segurança é uma parte fundamental do seu design, que inclui sandboxing e ser orientado a capacidades (você precisa pedir permissões explicitamente para coisas como arquivos ou sockets). O WASI também tem o potencial de permitir uma fácil integração entre linguagens. Solomon Hykes, cofundador do Docker, escreveu em 2019: "Se o WASM+WASI existisse em 2008, não teríamos precisado criar o Docker"

Qual escolher

  • Se você nunca trabalhou com uma linguagem assembly, o WebAssembly provavelmente é a linguagem mais fácil para começar. Dito isso, se você quer aprender uma linguagem assembly que compila para código de máquina, experimente o Assembly MIPS
  • Se você está trabalhando em uma máquina x86-64 (muito provável), experimente o Assembly x86-64
  • Se você conhece LISP, vai gostar de saber que o WebAssembly usa S-expressions
  • Se você trabalha com aplicações web, o WebAssembly é a opção mais lógica
  • Se você se importa com desempenho, x86-64 e MIPS são ótimas opções. Ou, se você se importa com desempenho na web, experimente o WebAssembly
Translation missing: pt-BR.number.nth.ordinalized Nov 2023 · Achou útil?