Introdução: Por que usar cache em C++?
No desenvolvimento moderno em C++, a velocidade da aplicação é frequentemente um fator crítico. O cache permite reduzir significativamente o tempo de acesso aos dados, diminuir a carga nos bancos de dados e acelerar a execução de operações computacionais repetitivas. Neste artigo, vamos explorar detalhadamente três abordagens principais para cache em C++: uso de sistemas externos (Redis e Memcached) e soluções integradas disponíveis "prontas para uso".
Independentemente de você estar desenvolvendo um servidor web de alta carga, um motor de jogos ou um sistema financeiro, a escolha correta da estratégia de cache pode aumentar o desempenho dezenas de vezes. Analisaremos os prós e contras de cada abordagem, além de fornecer exemplos de código prontos para integração rápida.
1. Soluções integradas para cache em C++
Antes de conectar serviços externos, vale a pena considerar as capacidades que a própria linguagem C++ e sua biblioteca padrão oferecem. As soluções integradas são ideais para aplicações single-thread ou para casos em que os dados não precisam ser compartilhados entre vários processos.
1.1. Cache LRU simples baseado em std::unordered_map
O algoritmo LRU (Least Recently Used) é um dos mais populares para cache integrado. Ele remove automaticamente os elementos menos usados quando o cache atinge o limite de memória.
#include <unordered_map>
#include <list>
#include <mutex>
template <typename Key, typename Value>
class LRUCache {
private:
size_t capacity_;
std::list<Key> items_;
std::unordered_map<Key, std::pair<Value, typename std::list<Key>::iterator>> cache_;
std::mutex mutex_;
public:
LRUCache(size_t capacity) : capacity_(capacity) {}
void put(const Key& key, const Value& value) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
items_.erase(it->second.second);
cache_.erase(it);
}
items_.push_front(key);
cache_[key] = {value, items_.begin()};
if (cache_.size() > capacity_) {
auto last = items_.back();
items_.pop_back();
cache_.erase(last);
}
}
std::optional<Value> get(const Key& key) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it == cache_.end()) return std::nullopt;
// Move a chave para o início da lista
items_.splice(items_.begin(), items_, it->second.second);
return it->second.first;
}
};
Vantagens: sobrecarga mínima, sem latência de rede, controle total sobre os dados.
Desvantagens: os dados não são compartilhados entre threads (requer sincronização manual), limitados pela memória de um único processo.
1.2. Uso de std::shared_mutex para multithreading
Para aplicações multithread, é possível melhorar o desempenho usando shared_mutex, que permite leitura simultânea por várias threads:
#include <shared_mutex>
class ThreadSafeCache {
std::map<std::string, std::string> data_;
mutable std::shared_mutex mutex_;
public:
std::string read(const std::string& key) {
std::shared_lock lock(mutex_);
auto it = data_.find(key);
return (it != data_.end()) ? it->second : "";
}
void write(const std::string& key, const std::string& value) {
std::unique_lock lock(mutex_);
data_[key] = value;
}
};
2. Cache com Memcached em C++
Memcached é um sistema de cache distribuído em memória RAM, ideal para armazenar pequenos fragmentos
```