2 pontos por GN⁺ 2025-09-12 | Ainda não há comentários. | Compartilhar no WhatsApp
  • A instalação de pacotes do Bun funciona de forma extremamente rápida em comparação com gerenciadores de pacotes existentes
  • O ponto central dessa velocidade está em uma abordagem sob a ótica de programação de sistemas e na minimização de chamadas de sistema
  • O ganho de desempenho vem da aplicação de estratégias detalhadas, como código nativo baseado em Zig, uso de cache binário e otimizações específicas por sistema operacional
  • Até no processo de descompactação de tarballs e cópia de arquivos, ele adota métodos de alto desempenho que aproveitam as características do hardware
  • Também aumenta a eficiência do cache da CPU e a acessibilidade à memória por meio de otimizações em estruturas de dados, como grafo de dependências e lockfile

Por que o Bun Install é rápido

  • O bun install do Bun oferece, em média, uma instalação de pacotes 7 vezes mais rápida que npm, 4 vezes mais rápida que pnpm e 17 vezes mais rápida que yarn
  • Isso não se deve apenas a benchmark, mas ao fato de que o problema de instalação de pacotes foi abordado sob a ótica de programação de sistemas, e não de JavaScript
  • Otimizações de desempenho foram aplicadas ativamente em várias camadas, como minimização de chamadas de sistema, cache binário de manifests, otimização da extração de tarballs e cópia de arquivos nativa do sistema operacional

Limitações do Node.js e da arquitetura dos gerenciadores de pacotes

  • Desde o lançamento do Node.js em 2009, o modelo de IO assíncrono baseado em event loop e thread pool também se espalhou para os gerenciadores de pacotes
  • Na época, por causa das limitações de hardware, como discos lentos e redes lentas, a estratégia de IO assíncrono com alta frequência de chamadas de sistema fazia sentido
  • Porém, nos sistemas modernos, SSDs NVMe, redes rápidas e CPUs de alto desempenho já são comuns, e o gargalo real não é o IO, mas o overhead das chamadas de sistema

O custo de chamadas de sistema e troca de modo

  • Quando um programa solicita uma operação como leitura de arquivo, é preciso alternar de user mode para kernel mode, e esse processo consome ciclos valiosos da CPU (1000 a 1500 ciclos)
  • A instalação de pacotes exige, por natureza, dezenas ou centenas de milhares de chamadas de sistema, o que pode consumir vários segundos de tempo de CPU apenas com o custo de alternância entre operações
  • Por exemplo, ao instalar o React e suas dependências, o npm usa cerca de 1 milhão de chamadas de sistema, o yarn 4 milhões, o pnpm 500 mil e o bun 160 mil

Diferença de abordagem entre gerenciadores tradicionais e o Bun

  • npm, pnpm e yarn são todos baseados em Node.js, então o JavaScript precisa ser executado por várias camadas de abstração, como libuv, event loop, thread pool e intermediação de chamadas de sistema
  • Nesse processo, acabam se acumulando conversão de argumentos, filas do worker pool, ramificações de tarefas no event loop e chamadas de sistema futex (sincronização por lock), fazendo com que o gerenciamento dessas chamadas fique mais lento do que o próprio IO
  • Gerenciadores de pacotes feitos em Node.js têm dificuldade para alcançar desempenho próximo ao nativo por causa dessa limitação estrutural

Bun: motor de instalação nativo implementado em Zig

  • O Bun, escrito em Zig, chama diretamente as chamadas de sistema, pulando totalmente o motor JavaScript e as camadas de abstração
  • Por exemplo, a leitura de arquivo executa diretamente a chamada de sistema openat() no código Zig e retorna os dados imediatamente
  • Assim, a leitura de dezenas de milhares de arquivos acontece em altíssima velocidade, sem thread pool, event loop ou conversão de dados no meio do caminho
  • Em benchmark, o Bun consegue ler 146.057 arquivos package.json por segundo, enquanto o Node.js fica na casa dos 60 mil e é mais de 2 vezes mais lento

Gerenciamento de dependências e otimização de DNS

  • Ao executar bun install, o Bun aciona de forma assíncrona o prefetch de DNS ao mesmo tempo em que analisa as dependências
  • No macOS, por exemplo, ele usa a API assíncrona de DNS não oficial da Apple (getaddrinfo_async_start()), permitindo processar tarefas de rede em paralelo sem bloquear threads
  • Já os gerenciadores tradicionais, baseados no thread pool do libuv, acabam executando internamente código bloqueante, desperdiçando recursos

Cache binário de manifests de pacotes

  • npm e outros armazenam manifests em cache como JSON, mas o Bun faz o parse uma vez e depois salva o resultado em formato binário (arquivo .npm)
  • Isso minimiza duplicação de strings e overhead de parse, e na memória real os valores podem ser acessados diretamente apenas por cálculo de offset, sem criar novos objetos, sem novo parse e sem garbage collection
  • Com cabeçalhos ETag e If-None-Match, ele verifica apenas mudanças, validando atualizações sem parse desnecessário de dados
  • Em benchmark, a instalação via cache do Bun é mais rápida até do que uma instalação fresh do npm

Desempenho no processamento de tarballs

  • Gerenciadores de pacotes comuns recebem o tarball em stream, e sempre que falta espaço no buffer ocorrem realocações, cópias e redimensionamentos em sequência
  • O Bun recebe o tarball inteiro antes de desempacotar e usa os últimos 4 bytes do gzip para descobrir previamente o tamanho descomprimido, o que permite alocar memória apenas uma vez
  • Com uso de libdeflate e afins, ele descompacta mais rápido e elimina cópias redundantes e redimensionamentos desnecessários

Grafo de dependências e otimização de estruturas de dados

  • Gerenciadores tradicionais criam árvores de dependência com objetos JavaScript e ponteiros, o que espalha os dados aleatoriamente na memória e aumenta os cache misses da CPU com frequência (problema de pointer chasing)
  • O Bun aplica o padrão Structure of Arrays (SoA), armazenando todos os pacotes, strings e dependências em grandes blocos contínuos de memória
    • Com acesso baseado em offset/comprimento, a CPU consegue ler vários pacotes de uma vez por linha de cache, em uma estrutura amigável ao cache
    • O lockfile também, em vez de JSON/YAML, é armazenado de forma alinhada ao padrão SoA, facilitando deduplicação de strings e acesso sequencial à memória
  • O formato binário de lockfile (bun.lockb) também foi introduzido de forma experimental, mas foi trocado por um formato de texto mais legível por reduzir problemas de colaboração com Git

Otimização de cópia de arquivos por sistema operacional

macOS

  • Uso de clonefile: clona o diretório inteiro com um único system call em modo Copy-On-Write
  • Reduz o uso duplicado de espaço em disco e maximiza a velocidade de instalação
  • Se clonefile falhar, há fallback gradual para clonagem por diretório e depois para copyfile

Linux

  • Tenta hard link primeiro: em vez de criar um novo arquivo, cria apenas uma nova referência ao arquivo existente, sem mover dados no disco
  • Se hard link não for possível, em Btrfs/XFS aplica Copy-On-Write com ioctl_ficlone
  • Depois disso, faz fallback para copy_file_range, sendfile e, por fim, para o método comum de copyfile

Resumo geral

  • O Bun supera os limites tradicionais de desempenho de gerenciadores de pacotes por meio de minimização de chamadas de sistema, estruturas binárias, otimizações por sistema operacional e melhorias em estruturas de dados
  • Com isso, além de instalação ultrarrápida, também oferece ganhos de eficiência em memória e CPU
  • Em comparação com gerenciadores baseados em Node.js, ele pode ser aplicado a projetos sem necessidade de trocar o runtime separadamente, mantendo a compatibilidade
  • Ele proporciona a experiência de reduzir processos de instalação que levavam minutos em codebases grandes para algo entre milissegundos e poucos segundos
  • É um excelente caso de estudo e referência de otimização sob medida para o nível de sistema, hardware e sistema operacional

Ainda não há comentários.

Ainda não há comentários.