Se tem uma coisa que sempre me salva no dia a dia, é cache em memória.
Quando a latência aperta e a performance precisa voar, um bom cache faz toda a diferença!
Ao longo do tempo, tive a oportunidade de testar várias implementações: go-cache, FreeCache, Ristretto, FastCache… e o mais legal do Go é que a inovação não para, sempre surgem novas opções!
Então, bateu aquela curiosidade e um pouco de desafio claro 😁, e para deixar tudo ainda mais empolgante, voltei a colaborar no projeto open source do Quick! 🚀
O Quick, é um roteador leve feito em Go, que está evoluindo para se tornar um framework web completo! 💡🔥
E fiquem ligados… em breve trago mais novidades sobre ele! 😍
O Quick está precisando de um middleware de cache, isso mesmo acreditem 🤣, e um dos seus pilares é não ter dependências externas, mantendo sua abordagem minimalista.
Juntando tudo isso, eu não aguentei… e quem me conhece sabe bem como eu sou 🤣🤣.
Resolvi reviver o que havia feito há um ano, testar novas ideias e evoluir o gcache para gocache. Já faz um tempo que venho brincando nas madrugadas com alguns algoritmos de cache e até gravei um vídeo sobre isso, acredita ? De tão gostoso é brincar com esta estrutura de dados.
📌 Quer ver na prática? Confere aqui => gcache no YouTube 🎥
O resultado do que fiz ? Tá tudo no post abaixo! 👇 Espero que gostem 🚀😃.
O que é Cache em Memória ?
O cache em memória é uma técnica essencial para otimização de desempenho em sistemas computacionais. Ele consiste no armazenamento temporário de dados diretamente na RAM, reduzindo a necessidade de acessos repetidos a fontes mais lentas, como bancos de dados ou sistemas de arquivos.
Ao manter os dados mais acessados na memória, eliminamos o gargalo de I/O (entrada e saída), permitindo respostas extremamente rápidas e um uso mais eficiente dos recursos computacionais. Lindo, não é? Para alcançar essa eficiência, é essencial utilizar linguagens de programação que ofereçam um gerenciamento robusto e controle eficiente sobre esses processos.
A linguagem que escolhi, alguém tem dúvidas ? Claro, foi o Go. ❤️
Embora o Go não seja uma linguagem de gerenciamento manual de memória como C ou Rust, ele ainda oferece um equilíbrio poderoso entre simplicidade, eficiência e controle de recursos. Mesmo contando com um Garbage Collector (GC), sua baixa latência e otimizações constantes permitem a criação de sistemas de cache em memória altamente performáticos, e vamos ver isso logo abaixo na prática.
Go se destaca por seu modelo de concorrência eficiente, utilizando goroutines e sync.Mutex / RWMutex para garantir acesso rápido e seguro ao cache, minimizando contenção e maximizando a escalabilidade.
Seu runtime altamente otimizado, aliado à capacidade de gerar binários leves e de alta performance, faz dele uma escolha excepcional para aplicações que exigem respostas rápidas, baixo consumo de CPU e um uso eficiente da RAM.
Quer saber mais sobre esse tópico ?
Escrevi alguns artigos sobre o assunto, e se você curte desempenho e concorrência em Go, recomendo dar uma olhada nesses dois:
📌 “Um pouco sobre Goroutines” → Explorando a magia das goroutines e como elas ajudam a escalar aplicações com eficiência.
📌 “Workloads: CPU-Bound e IO-Bound” → Um guia essencial para entender e otimizar cargas de trabalho no Go.
Espero que gostem! 😌🤗
O Go é a ferramenta fantástica, e para construirmos soluções robustas e simples ❤️.
O que é gocache ?
O gocache é um cache de memória fragmentada de alto desempenho para aplicativos Go, projetado para velocidade e eficiência. Ele foi otimizado para operações simultâneas de leitura e gravação, aproveitando fragmentação inteligente, otimizações de bloqueio (mutex) e gerenciamento eficiente de expiração para superar outras soluções de cache baseadas em Go.
A implementação do gocache é uma evolução do gcache, que comecei há um ano, inicialmente como um projeto didático e acadêmico. Desde então, fiz diversas versões diferentes de forma didática, refinando a arquitetura até chegar no gocache 😍.
Dentre as versões existe uma feita toda com Generics antes que me perguntem 🤗 e foi criado um outro repo chamado Benchmarking gocache é aqui que encontraram todas versões disponíveis e os tests que fiz até chegar em nossa V9 gocache , isso mesmo a versão que estamos usando hoje é a v9 🤗.
Um dos seus pilares é ser 100% minimalista e livre de dependências externas.
📌 gocache é uma biblioteca em Go, não um serviço como Redis!
Embora tanto o gocache quanto o Redis armazenem dados em memória, eles têm propósitos diferentes:
:
📌 gocache é uma LIB escrita em Go, projetada para ser embutida diretamente no seu código Go, funcionando como um cache local dentro da aplicação.
📌 Redis é um serviço externo que roda como um banco de dados em memória independente, permitindo cache distribuído e comunicação entre múltiplas instâncias.
Se você precisa de um cache ultrarrápido diretamente na sua aplicação Go, sem necessidade de conexões externas ou infraestrutura adicional, o gocache é a solução ideal! 🚀
Caso necessite de uma cache distribuído e compartilhado entre múltiplas aplicações, Redis pode ser uma melhor escolha.
Quer contribuir, sugerir melhorias ou compartilhar novas ideias? Fique à vontade! 🚀 Aprender e evoluir faz parte do processo, e vou adorar discutir otimizações e novas possibilidades! 😃
Podem enviar aqui issues gocache 🤗.
gocache vs. go-cache vs. freecache
Para validar o desempenho do gocache, realizamos benchmarks comparativos contra duas das bibliotecas de cache em memória mais conhecidas no ecossistema Go.
go-cache
Amplamente utilizado, fornece um cache baseado em mapa com controle de expiração.
freecache
Um cache baseado em segmentos de memória, eficiente para workloads de alta concorrência.
Cenário de Teste
Os benchmarks foram executados simulando um ambiente de alta demanda, onde diversas operações de SET e GET são realizadas de forma concorrente.
O objetivo era avaliar a eficiência da escrita (SET) a quantidade de operações por segundo e tempo médio de execução. A eficiência da leitura (GET) e o tempo médio de recuperação dos dados. O impacto da concorrência como o cache responde sob alta carga.
Benchmark
Resultados dos Benchmarks (3 segundos de execução)
LIBS. SET Ops SET ns/op GET Ops GET ns/op Observações
gocache 24,317,970 248.9 ns/op 14,749,851 270.8 ns/op 🏆 Very fast write, good read
go-cache 16,311,879 389.9 ns/op 13,021,398 277.5 ns/op 🚀 Excellent reads, slow writes
freecache 23,577,146 293.9 ns/op 8,554,900 395.8 ns/op ❌ Decent write, slow read
O gocache foi a implementação mais rápida em escrita (SET), conseguindo 24,3M de operações em 3s com um tempo médio de 248.9 ns/op, superando go-cache (389.9 ns/op) e FreeCache (293.9 ns/op). Esse desempenho demonstra a eficiência da estratégia de shards e mutexes bem otimizados, minimizando contenção e garantindo operações mais rápidas.
O go-cache teve um desempenho razoável na leitura, processando 13M de GETs em 3s com um tempo médio de 277.5 ns/op. Apesar de ser significativamente mais lento na escrita, seu modelo baseado em mapas de memória com RWMutex ajuda a reduzir o overhead em workloads voltadas para leitura de dados.
O FreeCache apresentou o pior tempo de leitura, com um GET médio de 395.8 ns/op, tornando-o menos eficiente para cargas de trabalho intensivas em leituras. Apesar de ser competitivo na escrita (23,5M de SETs em 3s), sua abordagem baseada em segmentação de memória pode resultar em fragmentação e maior overhead ao recuperar os dados armazenados.
O gocache demonstrou ser a melhor escolha para cenários onde a velocidade de escrita é crítica, sendo cerca de 36% mais rápido que o go-cache e 18% mais rápido que o FreeCache em operações de SET. Além disso, o gocache manteve uma boa performance em GETs, tornando-se uma solução balanceada e eficiente para aplicações de cache em memória. 🚀
Todos os testes detalhados podem ser encontrados no repositório:
gocache X go-cache X freecache
Exemplo
Para utilizar o gocache, basta fazer a chamada direto do seu code Go
$ go mod init myprojet
$ go mod tidy
📌 Para acessar basta entrar aqui gocache 🚀
O gocache utiliza uma estratégia baseada em shards e um ring buffer para garantir alto desempenho e evitar contenção excessiva de locks.
estrutura dados gocache
Cada shard é uma partição independente do cache, possuindo sua própria estrutura de sincronização (RWMutex) e um ring buffer para rastrear a expiração dos itens. O ring buffer permite um gerenciamento eficiente de itens expirados sem precisar percorrer todo o cache.
Algoritmos de Hash Mais Usados em Cache em Memória
Os algoritmos de hash desempenham um papel essencial no desempenho de caches em memória, sendo responsáveis por transformar chaves em identificadores únicos para recuperação eficiente de dados. Escolher o algoritmo certo pode influenciar diretamente a velocidade das operações de SET e GET, consumo de memória e concorrência.
📊 Comparação de Algoritmos de Hash
🧮 Algoritmo | ✅ Melhor Para | 🚀 Ponto Forte
🔹 FNV-1a | 🔑 Chaves curtas (≤10 caracteres) | ⚡ Baixo overhead e simplicidade
🔹 xxHash | 🏗️ Chaves longas (>10 caracteres) | ⚡ Rápido e eficiente strings longas
🔹 MurmurHash3 | 🎲 Estruturas de dados probabilis. | 🎯 Boa dispersão e baixa colisão
🔹 CityHash | 📚 Indexação de grandes volumes | 🏎️ Ótimo para CPUs modernas
🔹 MetroHash | 🌐 Telecom e redes | 📡 Alto desempenho tráfego massivo
O FNV-1a é uma variante do algoritmo Fowler-Noll-Vo, projetado para ser rápido e eficiente em CPUs modernas. Ele opera aplicando XOR e multiplicações para espalhar os bits da chave de forma uniforme.
fnv1aHash utilizado no gocache
O xxHash é um algoritmo altamente otimizado que processa dados em blocos de 4 ou 8 bytes, reduzindo significativamente a latência para chaves longas. Ele usa operações bitwise e multiplicações otimizadas para arquitetura moderna de CPU.
xxHash64
O MurmurHash3 usa um esquema de mistura de bits para produzir distribuições uniformes de hashes, minimizando colisões. Ele é altamente eficiente em estruturas como bloom filters e tabelas hash distribuídas.
O CityHash, desenvolvido pelo Google, é otimizado para arquiteturas x86 e ARM modernas, utilizando instruções vetorizadas (SSE e AVX) para aumentar a velocidade. Ele é altamente eficiente para grandes volumes de dados.
O MetroHash é um hash projetado para alta velocidade e baixa latência, sendo usado em redes de telecomunicações e aplicações que requerem alto throughput. Ele é baseado em técnicas de processamento paralelo para maximizar a performance
Conclusão
O uso de cache em memória é uma estratégia essencial para aplicações de alto desempenho. O gocache implementa um sistema eficiente baseado em shards, mutexes e um ring buffer para expiração de itens, garantindo baixo overhead. Durante os testes, analisamos diferentes técnicas de hashing e validamos a melhor abordagem para manter um desempenho consistente.
Ainda sigo observando e testando mais possibilidades a medida que vou estudando e aprendendo, recebo muitos comentários e sugestões, e vou testando e subindo em um repo de benchmarking que pode se encontrado aqui Benchmark gocache.
Se você trabalha com Go e precisa de um cache de alto desempenho, experimente o gocache e compartilhe seu feedback! 🚀
Espero que gostem 🚀🚀 ☺️
Top comments (0)