# How to Implement a Custom Eviction Algorithm in libCacheSim Using Plugins

> Learn to implement a custom eviction algorithm in libCacheSim using its plugin system. Create new cache replacement policies as shared libraries without core modifications.

- Repository: [Juncheng Yang/libcachesim](https://github.com/1a1a11a/libcachesim)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/1a1a11a/libcachesim/blob/main/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 the `obj_id_t` of 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`](https://github.com/1a1a11a/libcachesim/blob/main/example/plugin_v2/plugin_lru.cpp) but evicts the most recent object instead of the least recent.

```cpp
// 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`](https://github.com/1a1a11a/libcachesim/blob/main/example/plugin_v2/CMakeLists.txt). Copy this configuration and adjust the source filename:

```cmake
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:

```bash
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:

```c
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`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/include/libCacheSim/plugin.h) | Declares the V1 and V2 plugin interfaces, including hook typedefs and creation helpers. | [plugin.h](https://github.com/1a1a11a/libcachesim/blob/develop/libCacheSim/include/libCacheSim/plugin.h) |
| [`libCacheSim/cache/eviction/plugin_cache.c`](https://github.com/1a1a11a/libcachesim/blob/main/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](https://github.com/1a1a11a/libcachesim/blob/develop/libCacheSim/cache/eviction/plugin_cache.c) |
| [`example/plugin_v2/plugin_lru.cpp`](https://github.com/1a1a11a/libcachesim/blob/main/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](https://github.com/1a1a11a/libcachesim/blob/develop/example/plugin_v2/plugin_lru.cpp) |
| [`example/plugin_v1/plugin_lru.c`](https://github.com/1a1a11a/libcachesim/blob/main/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)](https://github.com/1a1a11a/libcachesim/blob/develop/example/plugin_v1/plugin_lru.c) |

## 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_hook`** function is the critical component where you implement your replacement policy logic, returning the `obj_id_t` of the object to evict.
- Plugins are compiled as **shared libraries** (`.so` files) and loaded at runtime via the `plugin_path` parameter in `create_cache_using_plugin()`.
- Reference implementations in [`example/plugin_v2/plugin_lru.cpp`](https://github.com/1a1a11a/libcachesim/blob/main/example/plugin_v2/plugin_lru.cpp) and the interface definitions in [`libCacheSim/include/libCacheSim/plugin.h`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/include/libCacheSim/plugin.h) provide 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`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/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.