fiscal-rsfiscal-rs

Benchmarks

Comparação de performance entre Rust, Bun/TypeScript e PHP em condições controladas

Metodologia

Todos os benchmarks rodam em containers Docker idênticos com recursos controlados:

ParâmetroValor
CPU1 core (--cpus=1)
Memória512 MB (--memory=512m)
Rust1.85 (release mode, LTO)
Bunlatest
PHP8.3-cli

Cada container clona o repositório correspondente do GitHub, garantindo reprodutibilidade total. Qualquer pessoa pode rodar:

git clone https://github.com/JoaoHenriqueBarbosa/fiscal-rs
cd fiscal-rs
./benchmarks/run_all.sh

Os três runtimes testam as mesmas 12 operações com os mesmos dados de entrada, mesmas iterações (1M para operações rápidas, 100K para médias, 10K para pesadas), e 10% de warmup.

Resultados

Tabela comparativa

OperaçãoRustBunPHPRust vs BunRust vs PHP
format_cents_272 ns70 ns178 ns~1x2.5x
format_rate_4125 ns60 ns64 ns0.5x0.5x
escape_xml_clean40 ns111 ns124 ns2.8x3.1x
escape_xml_dirty87 ns4 ns154 ns0.04x1.8x
tag_simple_text122 ns224 ns1.039 µs1.8x8.5x
get_state_code15 ns4 ns268 ns0.3x18x
tag_nested_item3.0 µs7.0 µs9.7 µs2.3x3.2x
serialize_icms00829 ns1.021 µs3.325 µs1.2x4.0x
create_icms_totals0.2 ns4 ns255 ns19x1.277x
merge_totals_1013 ns61 ns400 ns4.8x31x
invoice_builder27 µs49 µs427 µs1.8x16x
sign_xml996 µs1.917 ms3.411 ms1.9x3.4x

Gráficos

Operações rápidas (< 200 ns)

Operações médias (200 ns — 50 µs)

Operações pesadas (> 50 µs)

Speedup do Rust (onde Rust é mais rápido)

Análise

Onde Rust domina

  • Operações pesadas (invoice builder, sign_xml): Rust é consistentemente 1.8–2x mais rápido que Bun e 3–16x mais rápido que PHP. São as operações que mais importam — gerar e assinar uma NF-e.
  • Criação de structs (create_icms_totals): 0.2 ns vs 4 ns (Bun) vs 255 ns (PHP). Structs em Rust são alocadas na stack sem GC.
  • XML nesting (tag_nested_invoice_item): 3 µs vs 7 µs vs 10 µs. String concatenation em Rust é previsível e sem GC pauses.

Onde Bun surpreende

  • escape_xml_dirty: 4 ns no Bun vs 87 ns no Rust. Bun/V8 usa SIMD strings otimizadas para replace de caracteres comuns.
  • get_state_code: 4 ns no Bun vs 15 ns no Rust. V8 JIT inline-cachea lookups em objetos pequenos.
  • format_rate_4: 60 ns no Bun vs 125 ns no Rust. Number.toFixed() do V8 é altamente otimizado.

Contexto

Bun é impressionantemente rápido para um runtime com GC — em micro-operações ele pode até superar Rust graças ao JIT do V8. Mas nas operações compostas que simulam uso real (gerar XML de NF-e, assinar com certificado), Rust mantém vantagem consistente de 1.8–2x, sem variância de GC e com uso de memória previsível.

A vantagem real do Rust, porém, não está só na velocidade: está na portabilidade via FFI. O mesmo código que roda em 27 µs para gerar uma NF-e pode ser chamado de Python, Node.js, WebAssembly, Kotlin e Swift — sem reescrever.

Reproduzindo

# Requisitos: Docker
./benchmarks/run_all.sh

# Resultados ficam em:
# benchmarks/results/rust.json
# benchmarks/results/bun.json
# benchmarks/results/php.json

Cada Dockerfile clona seu repositório:

  • Rust: github.com/JoaoHenriqueBarbosa/fiscal-rs
  • Bun: github.com/JoaoHenriqueBarbosa/FinOpenPOS
  • PHP: github.com/JoaoHenriqueBarbosa/sped-nfe (branch benchmarks)

On this page