Introduction : Pourquoi la mise en cache en C++ ?
Dans les logiciels modernes à forte charge, la vitesse d'accès aux données est cruciale. Qu'il s'agisse d'un serveur web traitant des millions de requêtes ou d'un terminal de trading haute fréquence, chaque accès superflu à une base de données ou à un disque lent peut devenir un goulot d'étranglement. C'est là qu'intervient la mise en cache — une technique qui consiste à stocker les données fréquemment demandées dans une mémoire rapide (RAM) pour un accès instantané ultérieur.
Le langage C++ offre au développeur des possibilités uniques pour construire des systèmes de mise en cache efficaces : du contrôle fin de la mémoire à l'utilisation d'outils externes puissants. Dans cet article, nous passerons des solutions intégrées simples aux systèmes industriels comme Redis et Memcached, et nous verrons quand et quelle approche choisir.
Vous apprendrez :
- Comment implémenter un cache simple en C++ sans bibliothèques tierces.
- Comment connecter et utiliser Redis et Memcached.
- La comparaison des performances et des scénarios d'application de chaque approche.
Solutions intégrées : Votre propre cache en C++
Avant de connecter des services externes, il est bon de comprendre que pour de nombreuses tâches, un cache intégré est suffisant. Les principaux avantages : latence réseau nulle, simplicité de déploiement et contrôle total sur les données.
Cache simple avec std::unordered_map
L'option la plus basique consiste à utiliser un tableau associatif. Cependant, il ne dispose d'aucun mécanisme d'éviction (eviction policy) et croîtra indéfiniment, ce qui peut entraîner un débordement de mémoire.
#include <unordered_map>#include <string>
class SimpleCache { std::unordered_map<std::string, std::string> cache_;public: void put(const std::string& key, const std::string& value) { cache_[key] = value; } std::string get(const std::string& key) { auto it = cache_.find(key); if (it != cache_.end()) { return it->second; } return ""; // ou lever une exception }};Cache LRU (Least Recently Used)
Une solution plus avancée est le cache LRU, qui supprime les éléments les moins utilisés lorsque la limite est atteinte. L'implémentation nécessite une combinaison d'une table de hachage et d'une liste doublement chaînée.
#include <list>#include <unordered_map>
template<typename K, typename V>class LRUCache { size_t capacity_; std::list<std::pair<K, V>> items_; std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> cache_;
public: LRUCache(size_t cap) : capacity_(cap) {}
V get(const K& key) { auto it = cache_.find(key); if (it == cache_.end()) { throw std::runtime_error("Key not found"); } items_.splice(items_.begin(), items_, it->second); return it->second->second; }
void put(const K& key, const V& value) { auto it = cache_.find(key); if (it != cache_.end()) { it->second->second = value; items_.splice(items_.begin(), items_, it->second); return; } if (items_.size() == capacity_) { auto last = items_.back(); cache_.erase(last.first); items_.pop_back(); } items_.emplace_front(key, value); cache_[key] = items_.begin(); }};Un tel cache est idéal pour fonctionner dans un seul processus, lorsque les données appartiennent uniquement à ce processus et ne nécessitent pas de synchronisation.
Memcached : Cache distribué pour données simples
Memcached est un système de mise en cache léger et hautes performances fonctionnant via les protocoles TCP/UDP. Il stocke les données en mémoire vive et ne prend pas en charge la persistance (persistence). Un excellent choix pour la mise en cache de ré