aviso: o site está em processo de internacionalização. você ainda pode encontrar erros de digitação ou trechos não traduzidos.

‹ todo o trabalho

agni

rust · go · kotlin

um 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.

scenariogorustdelta
PING262,976478,3151.8x
SET193,132466,3592.4x
GET (hit)260,325470,3381.8x
GET (miss)270,682466,4581.7x
Mixed SET+GET201,805451,2372.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.

ver o código no github ›