Arquitetura ARM vs x86: ISA, RISC vs CISC e a dor do multi-arch
ARM vs x86 explicado de verdade: o que é o instruction set, por que RISC vs CISC borrou, eficiência vs legado e como matar o exec format error com builds multi-arch.
Você fez docker pull da sua imagem favorita no MacBook novo, rodou docker run e levou um exec format error na cara. Ou pior: o build passou redondo na sua máquina, subiu pro CI e quebrou no servidor ARM da AWS Graviton com a mesma mensagem enigmática. Nenhum dos dois é bug do seu código. É o mundo te lembrando, da pior forma possível, que existem duas famílias de CPU dominantes e elas não falam a mesma língua de máquina.
Este artigo explica o que separa ARM de x86 de verdade, por que a divisão clássica RISC vs CISC já não significa o que você aprendeu na faculdade, e o que isso muda na sua vida prática de quem builda e deploya software.
A diferença real está no instruction set
A coisa que separa ARM de x86 não é velocidade, nem consumo, nem marca. É o instruction set architecture (ISA): o contrato entre o software e o silício, o conjunto de instruções binárias que aquele processador sabe executar. Um binário compilado para x86 é uma sequência de opcodes que só a família x86 entende. ARM tem outro vocabulário. São duas línguas distintas, e nenhuma CPU traduz a outra de graça.
x86 nasceu na Intel em 1978, e ARM apareceu nos anos 80 com uma filosofia oposta. x86 é CISC (Complex Instruction Set Computing): muitas instruções, algumas fazendo coisas elaboradas em um único opcode, tamanhos de instrução variáveis. ARM é RISC (Reduced Instruction Set Computing): poucas instruções simples, tamanho fixo, a ideia de que o compilador monta operações complexas a partir de tijolos simples e o hardware fica enxuto.
Por que a linha RISC vs CISC borrou
Aqui está a parte que os tutoriais antigos não contam: por dentro, um chip x86 moderno não é mais "puro CISC". Ele tem um decoder na frente que pega aquelas instruções complexas e variáveis e as quebra em micro-ops — operações pequenas, regulares, parecidas com RISC, que o core de fato executa. Ou seja, o x86 paga o custo de traduzir CISC para algo RISC-like em hardware, a cada ciclo. ARM, por ser RISC nativo, pula essa etapa de tradução.
Então a briga "RISC é mais simples" virou mais sutil. A ISA externa do x86 ainda é CISC (e carrega décadas de legado: instruções que ninguém usa mais, modos de operação herdados do 8086), mas o motor por baixo convergiu. O que sobra de diferença real é: x86 gasta transistores e energia naquele decoder de instrução complexa e de tamanho variável; ARM não. Em escala de bilhões de operações, esse overhead vira watts.
Eficiência energética vs performance de legado
E por isso que ARM dominou o mobile: quando cada miliwatt sai da bateria, um decoder mais simples e instruções de tamanho fixo (mais fáceis de buscar e despachar) são vantagem direta. O celular no seu bolso é ARM. O Apple Silicon (M1, M2, M3...) é ARM, e mostrou que a arquitetura escala para laptop e desktop sem virar um forno. A AWS Graviton levou ARM pro datacenter, oferecendo melhor performance por watt — e, na prática, instâncias mais baratas pro mesmo trabalho.
x86 não perdeu por isso. Onde a conta é "performance bruta de single-thread, custe o que custar" — workstations, servidores de banco de dados pesados, jogos — x86 (Intel e AMD) ainda briga de igual pra igual ou ganha. E tem o legado: décadas de software compilado só para x86, drivers, aplicações Windows corporativas que ninguém vai recompilar. Esse legado é um fosso que segura x86 firme no desktop e em boa parte dos servidores.
Se você quer entender por que "mais cores" nem sempre resolve e como single-thread ainda manda em muita carga, vale ler como núcleos e threads de CPU realmente funcionam — é a base que torna toda essa comparação menos abstrata.
A dor prática: binários são arch-specific
Aqui a teoria vira dor de cabeça real. Um binário não é portátil entre ISAs. Aquele exec format error é literalmente o kernel dizendo "esses opcodes não são da minha família de CPU". Você pode checar em que arquitetura está com um comando:
uname -m
# x86_64 -> você está em x86 (64 bits)
# arm64 / aarch64 -> você está em ARM (64 bits)
Imagens Docker carregam binários compilados. Uma imagem linux/amd64 puxada num host ARM não roda nativamente. O Mac M-series resolve no dia a dia com o Rosetta 2, que traduz x86 para ARM em tempo de execução; o Docker Desktop usa qemu para emular a outra arquitetura. Emulação funciona, mas tem custo: mais lenta, mais consumo, às vezes bugs sutis em código sensível à arquitetura. Emulação é muleta, não solução.
Multi-arch: pare de buildar só para a sua máquina
A solução de verdade é parar de assumir que o mundo é x86. Você builda a imagem para as duas arquiteturas e o registry serve a certa para cada host automaticamente, via manifest list. Com docker buildx:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t seu-usuario/sua-imagem:tag \
--push .
Uma imagem, dois alvos, zero exec format error no deploy. Minha opinião sem rodeios: builde multi-arch agora, por padrão, mesmo que hoje você só deploye em x86. ARM na sala de servidores deixou de ser exótico há tempo, costuma ser mais barato pelo mesmo trabalho na Graviton, e o custo de adicionar --platform ao seu pipeline é ridiculamente menor do que migrar tudo depois sob pressão.
Entender essa divisão também ajuda quando você mexe com word size: 32 vs 64 bits muda o tamanho dos registradores e o range de endereçamento, e ver os números em binário e hexadecimal torna concreto o que "registrador de 64 bits" significa de fato.
Perguntas frequentes
ARM é mais rápido que x86?
Depende da carga. Em performance por watt, ARM costuma ganhar — por isso domina mobile e cresce no datacenter. Em single-thread bruto sem se importar com energia, x86 de topo (Intel/AMD) ainda briga de igual ou na frente. Não existe "mais rápido" universal; existe "mais rápido para esta carga, sob estas restrições de energia e custo".
Posso rodar software x86 num chip ARM?
Sim, via emulação ou tradução binária: Rosetta 2 no Apple Silicon, qemu no Docker, camadas equivalentes no Windows on ARM. Funciona para a maioria dos casos, mas com penalidade de performance e energia, e ocasionalmente bugs em código de baixo nível. Para produção séria, prefira binários nativos via build multi-arch.
O que é RISC e CISC hoje, na prática?
ARM é RISC (instruções simples, tamanho fixo); x86 é CISC (instruções complexas, tamanho variável, muito legado). Mas chips x86 modernos decodificam internamente para micro-ops RISC-like, então a fronteira borrou. A diferença prática que sobra é overhead de hardware e energia no decoder do x86, não "simplicidade" do programa que você escreve.
Preciso me preocupar com isso se só escrevo código em linguagem de alto nível?
Quase nunca no código em si — o compilador ou runtime cuida da ISA. A dor aparece no empacotamento e deploy: imagens Docker, binários pré-compilados, dependências nativas (npm com binários, wheels Python). É aí que exec format error te encontra. Buildar multi-arch resolve isso de uma vez.
O que levar para casa
ARM e x86 são duas ISAs incompatíveis: ARM RISC é eficiente em energia, x86 CISC com decoder de micro-ops e um oceano de legado. A linha RISC vs CISC borrou por dentro, mas a consequência prática não mudou — binários e imagens Docker são arch-specific, e emulação (Rosetta, qemu) é muleta, não plano. Com ARM já firme no datacenter via Graviton e Apple Silicon no desktop, assuma um mundo multi-arch: adicione --platform linux/amd64,linux/arm64 ao seu docker buildx hoje e nunca mais leve um exec format error no deploy.
- 01 O que é CDN: por que ela acelera sites no mundo inteiro Entenda o que é uma CDN, como edge servers e cache reduzem latência e por que ela acelera sites no mundo inteiro — além de DDoS, WAF e TLS.
- 02 Salt, pepper, bcrypt e Argon2id: como proteger senhas de verdade Em 2012, o LinkedIn expôs 117mi de senhas. SHA-1 sem salt — 90% quebradas em 4h. Entenda o que cada camada de proteção resolve e por que Argon2id é a escolha certa hoje.