How to Implement a Custom Eviction Algorithm in libCacheSim Using Plugins
libCacheSim supports custom eviction algorithms through a flexible plugin system that allows you to implement new cache replacement policies as shared libraries without modifying the core library.
The libCacheSim framework provides two distinct plugin APIs for extending its caching capabilities. Whether you need to prototype a new research algorithm or deploy a high-performance custom policy, you can implement a custom eviction algorithm in libCacheSim using plugins by creating a shared library that conforms to the defined hook interfaces.
Understanding the libCacheSim Plugin Architecture
libCacheSim offers two plugin APIs that trade off implementation complexity against performance and control:
V1 API: Full Cache Implementation
The V1 API requires you to implement a complete cache object that obeys the libCacheSim cache_t interface. This approach provides the highest performance and complete control over all cache operations, including memory layout and request handling. Use this API when you need to optimize every aspect of the cache behavior or when your algorithm requires specialized data structures that conflict with the generic cache implementation.
V2 API: Hook-Based Implementation
The V2 API (hook-based) is the recommended approach for most users. Instead of implementing the entire cache object, you define a set of five C hook functions that are called by the generic plugin cache implementation. This allows you to focus solely on the eviction logic while reusing libCacheSim’s existing cache logic for object storage, size accounting, and request routing. The V2 API significantly reduces boilerplate code and accelerates development of new replacement policies.
Implementing a Custom Eviction Algorithm with the V2 Hook API
To implement a custom eviction algorithm using the V2 API, you must provide specific hook functions that the core library will invoke during cache operations.
Required Hook Functions
The V2 API is defined in libCacheSim/include/libCacheSim/plugin.h (lines 99–115). Your shared library must export the following hooks:
cache_init_hook– Allocate and initialise your plugin’s internal state (e.g., priority queues, hash maps, or machine learning models).cache_hit_hook– Update metadata when a requested object is found in the cache.cache_miss_hook– Update metadata on a miss and optionally trigger insertion logic.cache_eviction_hook– Return theobj_id_tof the object that must be evicted. This is the core function where your custom eviction logic lives.cache_remove_hook– Clean up any per-object state when the core cache removes an entry.cache_free_hook– Free the global plugin data structure when the cache is destroyed.
The cache_eviction_hook Function
The cache_eviction_hook is the critical function for implementing custom replacement policies. It receives a pointer to your plugin’s private data (returned by cache_init_hook) and the current request. Your implementation must examine its internal structures and return the identifier of the object to be evicted. The core plugin cache then handles the actual removal from storage.
Example: Most-Recently-Used (MRU) Plugin
Below is a minimal skeleton demonstrating the required hooks for a custom Most-Recently-Used eviction policy. This mirrors the full LRU example in example/plugin_v2/plugin_lru.cpp but evicts the most recent object instead of the least recent.
// plugin_mru.cpp – compile as a shared library
#include <unordered_map>
#include <libCacheSim.h>
class MRUCache {
public:
struct Node {
obj_id_t id;
uint64_t size;
Node *prev, *next;
Node(obj_id_t i=0, uint64_t s=0) : id(i), size(s), prev(nullptr), next(nullptr) {}
};
std::unordered_map<obj_id_t, Node *> map;
Node *head, *tail; // head = most-recent, tail = least-recent
uint64_t max_bytes;
MRUCache(uint64_t cap) : max_bytes(cap) {
head = new Node(); tail = new Node();
head->next = tail; tail->prev = head;
}
~MRUCache() { while (head) { Node *tmp=head; head=head->next; delete tmp; } }
void promote(Node *n) { detach(n); insert_front(n); }
void insert_front(Node *n) { n->prev=head; n->next=head->next; head->next->prev=n; head->next=n; }
void detach(Node *n) { n->prev->next=n->next; n->next->prev=n->prev; }
void *init(const common_cache_params_t &p) { return this; }
void hit(const request_t *req) {
auto it = map.find(req->obj_id);
if (it != map.end()) promote(it->second);
}
void miss(const request_t *req) {
if (req->obj_size > max_bytes) return;
Node *n = new Node(req->obj_id, req->obj_size);
map[req->obj_id] = n;
insert_front(n);
}
obj_id_t evict(const request_t *) {
Node *victim = head->next; // evict the most recent
detach(victim);
map.erase(victim->id);
obj_id_t id = victim->id;
delete victim;
return id;
}
void remove(obj_id_t id) {
auto it = map.find(id);
if (it == map.end()) return;
detach(it->second);
delete it->second;
map.erase(it);
}
};
extern "C" {
void *cache_init_hook(const common_cache_params_t cparams) {
return new MRUCache(cparams.cache_size);
}
void cache_hit_hook(void *data, const request_t *req) {
static_cast<MRUCache*>(data)->hit(req);
}
void cache_miss_hook(void *data, const request_t *req) {
static_cast<MRUCache*>(data)->miss(req);
}
obj_id_t cache_eviction_hook(void *data, const request_t *req) {
return static_cast<MRUCache*>(data)->evict(req);
}
void cache_remove_hook(void *data, obj_id_t id) {
static_cast<MRUCache*>(data)->remove(id);
}
void cache_free_hook(void *data) {
delete static_cast<MRUCache*>(data);
}
}
Compiling and Loading Your Plugin
Once you have implemented the required hooks, you must compile the code into a shared library and load it at runtime.
Building the Shared Library
The project ships with a CMake target for the example LRU plugin in example/plugin_v2/CMakeLists.txt. Copy this configuration and adjust the source filename:
add_library(plugin_myalgo SHARED plugin_myalgo.cpp)
target_include_directories(plugin_myalgo PRIVATE ${LIBCACHESIM_INCLUDE_DIR})
target_link_libraries(plugin_myalgo ${LIBCACHESIM_LIBRARY})
Compile the plugin:
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make plugin_myalgo # produces libplugin_myalgo.so
Loading at Runtime
Load the plugin using the generic plugin cache via create_cache_using_plugin(). You only need to provide the path to the compiled .so file in the cache parameters:
const char *cache_params = "cache_size=1073741824,plugin_path=./build/libplugin_myalgo.so,cache_name=pluginCache";
cache_t *my_cache = create_cache_using_plugin("pluginCache", common_params, (void *)cache_params);
The plugin_path parameter tells libCacheSim which shared library to load. The core code that parses this string and binds the symbols lives in libCacheSim/cache/eviction/plugin_cache.c (see line 63 and the dlopen/dlsym logic starting at line 138).
After loading, run simulations exactly as you would with any built-in cache: pass the cache pointer to the trace processor, MRC profiler, or other libCacheSim APIs. The plugin cache forwards all get, insert, and evict calls to the hooks you supplied.
Key Source Files and Reference Implementation
The following files contain the definitive interfaces and reference implementations for libCacheSim plugins:
| File | Role | Link |
|---|---|---|
libCacheSim/include/libCacheSim/plugin.h |
Declares the V1 and V2 plugin interfaces, including hook typedefs and creation helpers. | plugin.h |
libCacheSim/cache/eviction/plugin_cache.c |
Generic "plugin cache" implementation that loads shared libraries, resolves hook symbols with dlopen/dlsym, and forwards cache operations. |
plugin_cache.c |
example/plugin_v2/plugin_lru.cpp |
Full reference implementation of a V2 hook-based LRU plugin demonstrating all required hooks and internal data structures. | plugin_lru.cpp |
example/plugin_v1/plugin_lru.c |
Example of the older V1 API where the plugin implements the entire cache object. Useful for maximum performance optimization. | plugin_lru.c (V1) |
Summary
- libCacheSim provides a flexible plugin architecture that allows you to implement custom eviction algorithms without rebuilding the core library.
- The V2 hook-based API is the recommended approach for most use cases, requiring only five to six C hook functions while reusing libCacheSim's cache management logic.
- The
cache_eviction_hookfunction is the critical component where you implement your replacement policy logic, returning theobj_id_tof the object to evict. - Plugins are compiled as shared libraries (
.sofiles) and loaded at runtime via theplugin_pathparameter increate_cache_using_plugin(). - Reference implementations in
example/plugin_v2/plugin_lru.cppand the interface definitions inlibCacheSim/include/libCacheSim/plugin.hprovide definitive guidance for implementation.
Frequently Asked Questions
What is the difference between V1 and V2 plugin APIs in libCacheSim?
The V1 API requires implementing a complete cache object that conforms to the cache_t interface, giving you full control over memory management and request handling for maximum performance. The V2 API uses a hook-based approach where you implement only specific callback functions (like cache_eviction_hook) that are invoked by the generic plugin cache implementation, making it faster to develop new policies while leveraging libCacheSim's existing cache infrastructure.
Do I need to rebuild libCacheSim to use a custom plugin?
No, you do not need to rebuild libCacheSim. The plugin system uses dynamic loading via dlopen and dlsym (implemented in libCacheSim/cache/eviction/plugin_cache.c) to load your shared library at runtime. You simply compile your plugin as a separate .so file and specify its path using the plugin_path parameter when creating the cache with create_cache_using_plugin().
Which hook function is responsible for the actual eviction decision?
The cache_eviction_hook is the specific function where your custom eviction logic executes. This hook receives your plugin's private data structure and the current request, and it must return the obj_id_t of the object to be evicted. The generic plugin cache in plugin_cache.c then handles the physical removal of that object from the cache storage.
Can I implement a learning-based eviction policy using libCacheSim plugins?
Yes, the V2 hook API supports complex policies including learning-based or ML-driven eviction algorithms. You can maintain sophisticated data structures (neural network weights, feature vectors, or historical access statistics) in your plugin's private data structure initialized via cache_init_hook. The cache_eviction_hook can then run inference or lookup logic to select victims, while cache_hit_hook and cache_miss_hook update your model's state based on access patterns.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →