Skip to content

Commit 53150da

Browse files
authored
Add CAR (Clock with Adaptive Replacement) policy documentation and im… (#16)
* Add CAR (Clock with Adaptive Replacement) policy documentation and implementation - Introduced the CAR policy, combining ARC-like adaptivity with Clock mechanics to enhance concurrency and reduce overhead. - Added detailed documentation outlining the architecture, key operations, performance trade-offs, and example usage. - Updated the policy README to include CAR, highlighting its benefits and when to use it. - Removed outdated CAR roadmap documentation to streamline policy references. * Add policy-car feature flag to Cargo.toml - Introduced the `policy-car` feature flag in `Cargo.toml` to enable the new CAR (Clock with Adaptive Replacement) eviction policy. - Updated the `policy-all` list to include `policy-car`, ensuring it is available for modular builds. - This change enhances the flexibility of the library by allowing users to selectively enable the CAR policy based on their workload requirements. * Fix typo in policy module feature flag - Corrected the feature flag name from `policy-card` to `policy-car` in the `mod.rs` file to ensure consistency with the previously introduced CAR eviction policy. - This change aligns the module declaration with the intended feature flag, enhancing clarity and preventing potential build issues.
1 parent 6a4e196 commit 53150da

7 files changed

Lines changed: 1529 additions & 49 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ policy-all = [
4949
"policy-two-q",
5050
"policy-s3-fifo",
5151
"policy-arc",
52+
"policy-car",
5253
"policy-lifo",
5354
"policy-mfu",
5455
"policy-mru",
@@ -67,6 +68,7 @@ policy-heap-lfu = []
6768
policy-two-q = []
6869
policy-s3-fifo = []
6970
policy-arc = []
71+
policy-car = []
7072
policy-lifo = []
7173
policy-mfu = []
7274
policy-mru = []

docs/policies/README.md

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,25 @@ If you can only implement one “general purpose” policy for mixed workloads,
3434

3535
### Implemented Policies (CacheKit)
3636

37-
Enable the corresponding feature flag for each policy. See [Compatibility and Features](../guides/compatibility-and-features.md).
38-
39-
| Policy | Feature | Summary | Doc |
40-
|--------|---------|---------|-----|
41-
| LRU | `policy-lru` | Strong default for temporal locality | [LRU doc](lru.md) |
42-
| Fast LRU | `policy-fast-lru` | Optimized single-threaded LRU ||
43-
| MRU | `policy-mru` | Evicts most recent (niche: cyclic patterns) | [MRU doc](mru.md) |
44-
| SLRU | `policy-slru` | Segmented LRU with probation/protected | [SLRU doc](slru.md) |
45-
| LFU | `policy-lfu` | Frequency-driven, stable hot sets | [LFU doc](lfu.md) |
46-
| Heap-LFU | `policy-heap-lfu` | LFU with heap eviction | [Heap-LFU doc](heap-lfu.md) |
47-
| MFU | `policy-mfu` | Evicts highest frequency (niche/baseline) | [MFU doc](mfu.md) |
48-
| LRU-K | `policy-lru-k` | Scan-resistant recency | [LRU-K doc](lru-k.md) |
49-
| 2Q | `policy-two-q` | Probation + protected queues | [2Q doc](2q.md) |
50-
| ARC | `policy-arc` | Adaptive recency/frequency balance | [ARC doc](arc.md) |
51-
| FIFO | `policy-fifo` | Simple insertion-order (oldest first) | [FIFO doc](fifo.md) |
52-
| LIFO | `policy-lifo` | Stack-based (newest first) | [LIFO doc](lifo.md) |
53-
| Clock | `policy-clock` | Approximate LRU | [Clock doc](clock.md) |
54-
| Clock-PRO | `policy-clock-pro` | Scan-resistant Clock variant | [Clock-PRO doc](clock-pro.md) |
55-
| NRU | `policy-nru` | Coarse recency tracking | [NRU doc](nru.md) |
56-
| S3-FIFO | `policy-s3-fifo` | Scan-resistant FIFO | [S3-FIFO doc](s3-fifo.md) |
57-
| Random | `policy-random` | Baseline: uniform random eviction | [Random doc](random.md) |
37+
| Policy | Summary | Doc |
38+
|--------|---------|-----|
39+
| LRU | Strong default for temporal locality | [LRU doc](lru.md) |
40+
| MRU | Evicts most recent (niche: cyclic patterns) | [MRU doc](mru.md) |
41+
| SLRU | Segmented LRU with probation/protected | [SLRU doc](slru.md) |
42+
| LFU | Frequency-driven, stable hot sets | [LFU doc](lfu.md) |
43+
| Heap-LFU | LFU with heap eviction | [Heap-LFU doc](heap-lfu.md) |
44+
| MFU | Evicts highest frequency (niche/baseline) | [MFU doc](mfu.md) |
45+
| LRU-K | Scan-resistant recency | [LRU-K doc](lru-k.md) |
46+
| 2Q | Probation + protected queues | [2Q doc](2q.md) |
47+
| ARC | Adaptive recency/frequency balance | [ARC doc](arc.md) |
48+
| CAR | ARC-like with Clock (lower hit overhead) | [CAR doc](car.md) |
49+
| FIFO | Simple insertion-order (oldest first) | [FIFO doc](fifo.md) |
50+
| LIFO | Stack-based (newest first) | [LIFO doc](lifo.md) |
51+
| Clock | Approximate LRU | [Clock doc](clock.md) |
52+
| Clock-PRO | Scan-resistant Clock variant | [Clock-PRO doc](clock-pro.md) |
53+
| NRU | Coarse recency tracking | [NRU doc](nru.md) |
54+
| S3-FIFO | Scan-resistant FIFO | [S3-FIFO doc](s3-fifo.md) |
55+
| Random | Baseline: uniform random eviction | [Random doc](random.md) |
5856

5957
### Roadmap Policies (Planned)
6058

@@ -73,6 +71,7 @@ See [Policy roadmap](roadmap/README.md) for upcoming policies (LIRS, GDSF, TinyL
7371
- **LRU-K**: Good scan resistance; more metadata per entry.
7472
- **2Q**: Simple scan resistance; requires queue sizing.
7573
- **ARC**: Adaptive recency/frequency balance; no manual tuning; more metadata overhead.
74+
- **CAR**: ARC-like adaptivity with Clock; hits set ref bit only; scan-resistant.
7675
- **FIFO**: Predictable insertion order (oldest first); weak under strong locality.
7776
- **LIFO**: Stack order (newest first); niche use for undo buffers.
7877
- **Clock-PRO**: Scan-resistant Clock variant; more complexity.
@@ -83,10 +82,10 @@ For broader policy taxonomy (OPT, ARC, CAR, LIRS, Random, etc.), use the
8382

8483
## Practical Tradeoffs (What Changes In Real Systems)
8584

86-
- **Scan resistance**: `LRU`/`Clock` are vulnerable; `S3-FIFO`, `Heap-LFU`, `LRU-K`, `2Q`, and `ARC` handle scans better.
87-
- **Metadata & CPU**: `Random`/`FIFO` < `Clock` < `LRU` < `2Q`/`SLRU` < `LRU-K`/`ARC`/`LIRS`.
85+
- **Scan resistance**: `LRU`/`Clock` are vulnerable; `S3-FIFO`, `Heap-LFU`, `LRU-K`, `2Q`, `ARC`, and `CAR` handle scans better.
86+
- **Metadata & CPU**: `Random`/`FIFO` < `Clock` < `LRU` < `2Q`/`SLRU` < `LRU-K`/`ARC`/`CAR`/`LIRS`.
8887
- **Concurrency**: strict global `LRU` lists can contend; `Clock` and sharded designs often scale better.
89-
- **Adaptivity**: `LFU` needs decay to adapt; `ARC`-family adapts via history; static partitions (`2Q`/`SLRU`) need tuning.
88+
- **Adaptivity**: `LFU` needs decay to adapt; `ARC`/`CAR` adapt via history; static partitions (`2Q`/`SLRU`) need tuning.
9089
- **Predictability**: simpler policies are easier to reason about under tail-latency constraints; complex policies can have more edge cases.
9190

9291
## When To Use / Not Use (Rules Of Thumb)

docs/policies/car.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# CAR (Clock with Adaptive Replacement)
2+
3+
## Status
4+
5+
**Implemented** in `src/policy/car.rs`
6+
7+
## Goal
8+
9+
ARC-like adaptivity with Clock mechanics to reduce list manipulation overhead.
10+
Hits only set a reference bit instead of moving entries in linked lists,
11+
improving concurrency and cache-friendliness.
12+
13+
## Core Idea
14+
15+
Replace ARC's LRU lists with Clock structures plus ghost history:
16+
17+
- Two Clock rings for resident sets: **Recent** (seen once) and **Frequent** (repeated access)
18+
- Reference bits approximate recency within each set
19+
- ARC-like feedback from ghost hits adjusts `target_recent_size` (the adaptation parameter)
20+
- On hit: set ref bit only (no list move)
21+
- On eviction: sweep with clock hand, ref=0 evict, ref=1 clear and continue
22+
23+
## Core Data Structures
24+
25+
- `HashMap<K, usize>` for key → slot index
26+
- Single slot array with metadata: key, value, referenced bit, `Ring` (Recent/Frequent)
27+
- Two clock hands (`hand_recent`, `hand_frequent`) walking per-ring intrusive circular lists
28+
- Ghost lists `ghost_recent`, `ghost_frequent` (keys only) via `GhostList<K>`
29+
30+
## Key Operations (High Level)
31+
32+
### Get Operation
33+
34+
- Hit in Recent or Frequent ring: set `referenced = true`, return value (no list move)
35+
- Miss: not in cache (see insert for ghost hit handling)
36+
37+
### Insert Operation
38+
39+
- Key in cache: update value, set ref, return old value
40+
- Ghost hit in `ghost_recent`: adapt (increase `target_recent_size`), evict if needed, insert into Frequent ring
41+
- Ghost hit in `ghost_frequent`: adapt (decrease `target_recent_size`), evict if needed, insert into Frequent ring
42+
- Miss: evict if full (replace step), insert into Recent ring
43+
44+
### Replacement Step
45+
46+
Loop until one entry is evicted:
47+
48+
- If `|Recent| > target_recent_size`: sweep the Recent ring
49+
- Ref=0: evict to `ghost_recent`, done
50+
- Ref=1: demote to Frequent ring (clear ref), continue
51+
- If `|Recent| ≤ target_recent_size`: sweep the Frequent ring
52+
- Ref=0: evict to `ghost_frequent`, done
53+
- Ref=1: clear ref, advance hand, continue
54+
55+
### Adaptation
56+
57+
Same as ARC: ghost hit in `ghost_recent` increases `target_recent_size`;
58+
ghost hit in `ghost_frequent` decreases it.
59+
60+
## Complexity & Overhead
61+
62+
- O(1) get (hash lookup + bit set)
63+
- O(1) amortized insert
64+
- More metadata than plain Clock due to ghost lists and dual rings
65+
- Lower overhead than ARC on hits (no list moves)
66+
67+
## Example Usage
68+
69+
```rust
70+
use cachekit::policy::car::CARCore;
71+
use cachekit::traits::{CoreCache, ReadOnlyCache};
72+
73+
let mut cache = CARCore::new(100);
74+
cache.insert("page1", "content1");
75+
cache.insert("page2", "content2");
76+
assert_eq!(cache.get(&"page1"), Some(&"content1"));
77+
println!("recent: {}, frequent: {}, target: {}", cache.recent_len(), cache.frequent_len(), cache.target_recent_size());
78+
```
79+
80+
## When To Use
81+
82+
- Mixed workloads with unknown recency/frequency balance
83+
- Need scan resistance (ghost lists) like ARC
84+
- Prefer lower hit overhead than ARC (bit set vs list move)
85+
- Concurrency-friendly hit path
86+
87+
## When NOT To Use
88+
89+
- Simple temporal locality (Clock or LRU are simpler)
90+
- Memory-constrained (ghost lists add overhead)
91+
- Known workload where tuned 2Q/SLRU suffices
92+
93+
## Performance Characteristics
94+
95+
| Metric | Value |
96+
|----------|------------------|
97+
| Get | O(1) |
98+
| Insert | O(1) amortized |
99+
| Remove | O(1) |
100+
| Space | O(n) + ghost keys|
101+
| Scan res | High |
102+
| Adaptivity | Self-tuning |
103+
104+
## Implementation Notes
105+
106+
- Uses single slot array; Recent/Frequent are per-ring intrusive circular linked lists with separate hands
107+
- Demotion from Recent to Frequent clears ref bit (per CAR paper)
108+
- Ghost lists bounded to `capacity` each
109+
- `target_recent_size` starts at `capacity / 2`
110+
111+
## References
112+
113+
- Bansal & Modha, "CAR: Clock with Adaptive Replacement", FAST 2004
114+
- Wikipedia: https://en.wikipedia.org/wiki/Cache_replacement_policies

docs/policies/roadmap/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ If a policy moves into production code, its document should be moved back to
99

1010
## Roadmap Policies
1111

12-
- [CAR](car.md)
1312
- [GDSF](gdsf.md)
1413
- [LFU Aging](lfu-aging.md)
1514
- [LIRS](lirs.md)

docs/policies/roadmap/car.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)