Introduction: Why Caching in C++?
In modern C++ development, application speed is often a critical factor. Caching can significantly reduce data access time, lower the load on databases, and speed up the execution of repetitive computational operations. In this article, we will take a detailed look at three main approaches to caching in C++: using external systems (Redis and Memcached) and built-in solutions available "out of the box".
Whether you are developing a high-load web server, a game engine, or a financial system, choosing the right caching strategy can boost performance tenfold. We will break down the pros and cons of each approach and provide ready-made code examples for quick integration.
1. Built-in Caching Solutions in C++
Before connecting external services, it is worth considering the capabilities provided by the C++ language itself and its standard library. Built-in solutions are ideal for single-threaded applications or cases where data does not need to be shared between multiple processes.
1.1. Simple LRU Cache Based on std::unordered_map
The LRU (Least Recently Used) algorithm is one of the most popular for built-in caching. It automatically removes the least recently used items when the cache reaches its memory limit.
#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 the key to the front of the list items_.splice(items_.begin(), items_, it->second.second); return it->second.first; }};Advantages: minimal overhead, no network latency, full control over data.
Disadvantages: data is not shared between threads (manual synchronization required), limited by the memory of a single process.
1.2. Using std::shared_mutex for Multithreading
For multithreaded applications, performance can be improved by using shared_mutex, which allows multiple threads to read simultaneously:
#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. Caching with Memcached in C++
Memcached is a distributed in-memory caching system, ideally suited for storing small fragm