How to Integrate libCacheSim as a C Library into Existing Applications
Integrate libCacheSim into C applications by including the umbrella header libCacheSim.h, initializing a reader_t via open_trace() and a cache_t via algorithm-specific init functions like LRU_init(), then iterating with read_one_req() and cache->get() to compute miss ratios, linking against libCacheSim, glib-2.0, and zstd.
libCacheSim is a high-performance C library maintained at 1a1a11a/libcachesim that provides trace readers, request objects, and over 30 eviction algorithms for cache simulation. By linking against the shared library, you embed sophisticated cache modeling directly into existing applications without managing the underlying complexity of trace parsing or eviction state machines.
Core Architecture and Data Structures
The library centers on five opaque types declared in libCacheSim/include/libCacheSim/:
reader_t(defined inreader.h): Manages memory-mapped trace files and yieldsrequest_tobjects. Supports CSV, binary, and vscsi formats.request_t(defined inrequest.h): Encapsulates a single access with fields for timestamp, object ID, size, operation type, and TTL.cache_t(defined incache.h): Abstract cache object exposing function pointers forget(),put(),evict(), andcache_free().- Common parameters (
common_cache_params_t): Configures hash power, cache size, and metadata accounting. - Simulator API (declared in
simulator.h): High-level functions likesimulate_at_multi_sizes()for batch evaluations.
Include the single umbrella header to pull in all public definitions:
#include <libCacheSim.h>
Step-by-Step Integration Guide
Initialize the Trace Reader
Configure column mappings and open the trace file using open_trace(). In libCacheSim/include/libCacheSim/reader.h, this function accepts a reader_init_param_t structure:
reader_init_param_t params = default_reader_init_params();
params.obj_id_field = 5; // CSV column containing object ID
params.obj_size_field = 4; // CSV column containing object size
params.time_field = 2; // CSV column containing timestamp
params.has_header_set = true;
params.delimiter = ',';
reader_t *r = open_trace("path/to/trace.csv", CSV_TRACE, ¶ms);
The reader_t maintains an internal memory map of the file and metadata such as total request count.
Allocate a Request Object
Create a reusable request_t to avoid per-iteration heap allocations. The function new_request() is declared in request.h:
request_t *req = new_request();
This object is mutated by read_one_req() rather than reallocated on each call.
Instantiate a Cache Algorithm
Each eviction policy exposes an initialization function (e.g., LRU_init, ARC_init, S3FIFO_init) defined under libCacheSim/cache/eviction/. Pass common_cache_params_t to configure capacity:
common_cache_params_t cp = {
.cache_size = 1 * GiB,
.hashpower = 24,
.consider_obj_metadata = false
};
cache_t *c = LRU_init(cp, NULL); // Returns a concrete cache_t instance
The returned pointer implements the vtable defined in cache.h, providing uniform access to get(), put(), and eviction routines regardless of the specific algorithm.
Run the Simulation Loop
Iterate through the trace, invoking cache->get() to test for hits. A return value of NULL (or Boolean false) indicates a miss:
uint64_t n_req = 0, n_miss = 0;
while (read_one_req(r, req) == 0) {
if (!c->get(c, req)) {
n_miss++; // Object not resident; miss recorded
}
n_req++;
}
printf("miss ratio = %.4f\n", (double)n_miss / n_req);
The get() method internally updates metadata, inserts missing objects, and triggers evictions according to the policy implemented in the specific algorithm file (e.g., libCacheSim/cache/eviction/LRU.c).
Resource Cleanup
Release resources in reverse order of allocation to prevent memory leaks:
free_request(req);
c->cache_free(c); // Algorithm-specific destructor
close_reader(r);
Compiling and Linking Your Application
Build and Install libCacheSim
Clone and install the library system-wide to generate the pkg-config file:
git clone https://github.com/1a1a11a/libcachesim
cd libcachesim
mkdir _build && cd _build
cmake -G Ninja .. && ninja
sudo ninja install
This installs libCacheSim.so and libCacheSim.pc to standard system paths.
Compile Against the Library
Use pkg-config to inject correct include paths and linker flags. The library requires glib-2.0 and zstd as mandatory dependencies:
gcc my_app.c $(pkg-config --cflags --libs libCacheSim glib-2.0) -o my_app -lm -lzstd
Alternatively, specify flags manually:
gcc my_app.c -I/usr/local/include -lCacheSim -lglib-2.0 -lzstd -lm -o my_app
Advanced Integration Patterns
Multi-Size Cache Simulations
Evaluate the same trace against multiple cache sizes without manual file rewinding. The simulate_at_multi_sizes() function in simulator.h automates this:
guint64 sizes[] = { 1<<20, 10<<20, 100<<20 }; // 1 MiB, 10 MiB, 100 MiB
sim_res_t *res = simulate_at_multi_sizes(
r,
(cache_t*)LRU_init, // Initialization function pointer
3, // Number of sizes
sizes,
NULL, // Optional reader args
0.0, // Percentage to sample
1 // Number of threads
);
/* Access results: res[i].hit_cnt and res[i].miss_cnt per size */
Implementing Custom Eviction Policies
Extend libCacheSim with proprietary algorithms by implementing the cache_t interface. Create a source file defining your_policy_init() that returns a struct with populated function pointers for get, put, evict, and cache_free. Register the plugin via the infrastructure in libCacheSim/cache/plugin.c. Reference the example implementation at example/plugin_v1/plugin_lru.c.
Complete Minimal Example
The following program (simple_lru.c) demonstrates a fully functional integration:
#include <libCacheSim.h>
int main(void) {
/* 1. Configure CSV reader */
reader_init_param_t p = default_reader_init_params();
p.obj_id_field = 5;
p.obj_size_field = 4;
p.time_field = 2;
p.has_header_set = true;
p.delimiter = ',';
reader_t *r = open_trace("data/trace.csv", CSV_TRACE, &p);
/* 2. Allocate reusable request */
request_t *req = new_request();
/* 3. Initialize 1 GiB LRU cache */
common_cache_params_t cp = {
.cache_size = 1 * GiB,
.hashpower = 24,
.consider_obj_metadata = false
};
cache_t *c = LRU_init(cp, NULL);
/* 4. Run simulation */
uint64_t n_req = 0, n_miss = 0;
while (read_one_req(r, req) == 0) {
if (!c->get(c, req)) n_miss++;
n_req++;
}
printf("miss ratio: %.4f\n", (double)n_miss / n_req);
/* 5. Cleanup */
free_request(req);
c->cache_free(c);
close_reader(r);
return 0;
}
Compile and execute:
gcc simple_lru.c $(pkg-config --cflags --libs libCacheSim glib-2.0) -o simple_lru -lm -lzstd
./simple_lru
Summary
- Include the umbrella header
libCacheSim/include/libCacheSim.hto access all public APIs. - Initialize trace readers with
open_trace()andreader_init_param_tto map CSV columns or binary formats. - Create cache instances via algorithm-specific functions like
LRU_init(), passingcommon_cache_params_tfor sizing. - Iterate using
read_one_req()and test residency withcache->get(), counting misses for ratio calculations. - Link with
pkg-config --cflags --libs libCacheSim glib-2.0and add-lzstd -lmto resolve dependencies. - Extend functionality through the plugin API in
plugin.cfor custom eviction policies or usesimulate_at_multi_sizes()for batch analysis.
Frequently Asked Questions
What dependencies are required to build applications with libCacheSim?
libCacheSim requires GLib 2.0 and Zstandard (zstd). When compiling, pass flags for both libraries: pkg-config --cflags --libs libCacheSim glib-2.0 plus -lzstd -lm. These are mandatory runtime dependencies specified in the upstream CMakeLists.txt.
Can I use libCacheSim without reading from trace files?
Yes. While reader_t abstracts trace I/O, you can populate request_t objects manually and feed them directly to cache->get() or cache->put(). This is useful for hardware simulation or live caching scenarios where requests originate from runtime instrumentation rather than static files.
How do I select between different eviction algorithms at runtime?
Store function pointers to the initialization routines (e.g., LRU_init, ARC_init, S3FIFO_init) and invoke the desired constructor based on configuration. All algorithms return a cache_t* that exposes a uniform interface, allowing polymorphic cache behavior without changing the simulation loop logic.
Is libCacheSim thread-safe for parallel simulations?
The core cache_t implementations are not generally thread-safe for concurrent access to a single instance, but you can run independent simulations in parallel by creating separate reader_t and cache_t instances per thread. The simulate_at_multi_sizes() function accepts a thread-count parameter to parallelize evaluations across sizes safely.
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 →