agni
rust · go · kotlinum servidor de cache em memória implementado três vezes, a partir do mesmo desenho, para medir o que a linguagem realmente muda.
o problema
“qual linguagem é mais rápida?” é uma pergunta ruim porque quase nunca é feita com o mesmo desenho dos dois lados. as comparações que circulam normalmente medem programas diferentes, escritos por pessoas diferentes, resolvendo problemas parecidos.
agni tenta responder a versão honesta da pergunta: mantendo o mesmo protocolo de fio, a mesma forma de workload e o mesmo harness de benchmark, o que sobra de diferença quando só a linguagem muda?
arquitetura
um workspace cargo com quatro crates: agni (store, protocolo e comandos), agni-server (binário tcp), agni-client (cli) e agni-bench (benchmark). cliente e benchmark falam exatamente o mesmo protocolo do servidor, então a medição exercita o caminho real.
as três implementações vivem em um único clone, preservadas no histórico: main carrega a linha rust, e as versões go e kotlin ficam nas tags snapshot/go e snapshot/kotlin. nada de branches paralelas que apodrecem: é um git checkout para ter a árvore inteira de cada linguagem.
o escopo é deliberadamente pequeno: sem replicação, sem clustering, sem persistência, sem coordenação multi-nó. o objetivo não é competir com o redis, é isolar as variáveis que importam para a comparação.
decisões e trade-offs
`DashMap` em vez de `Arc<RwLock<HashMap>>`. com 50 conexões concorrentes disputando 1.000 chaves, o RwLock serializa toda escrita em um único lock. o dashmap fragmenta em 64 shards e ganha 28,5% de throughput em SET, com p99 caindo de 1,52ms para 1,07ms. o custo: cache miss puro ficou ~9% mais lento, porque um miss sob read lock é baratíssimo e a seleção de shard adiciona overhead. trade-off aceito, já que em cache bem aquecido o miss é o caminho raro.
parser sobre bytes em vez de `String::from_utf8_lossy`. Command::from_bytes passou a fatiar o slice direto, o que faz valores binários sobreviverem ao round trip. está no hot path por comando, então foi medido antes e depois.
benchmark intercalado A/B/A/B. a primeira tentativa rodou quatro execuções “depois” e depois quatro “antes”, e mostrou uma melhora aparente de 3 to 9%. era inteiramente deriva da máquina ao longo do lote. intercalando as execuções, o ganho real do parser ficou dentro do ruído. a mudança foi mantida por corretude, porque binário sobrevive ao round trip, e não por performance.
benchmarks
go vs rust, throughput em ops/seg, 50 conexões, 10.000 operações por cenário, loopback local, build release.
| scenario | go | rust | delta |
|---|---|---|---|
| PING | 262,976 | 478,315 | 1.8x |
| SET | 193,132 | 466,359 | 2.4x |
| GET (hit) | 260,325 | 470,338 | 1.8x |
| GET (miss) | 270,682 | 466,458 | 1.7x |
| Mixed SET+GET | 201,805 | 451,237 | 2.2x |
medido em 25/08/2026. a metodologia completa e o histórico ficam em BENCHMARK.md, no repositório. resultados são acrescentados, nunca substituídos, para que a série continue auditável.