feat(cache): add lru block cache
This commit is contained in:
1 parent
a84884777c
commit
30d276ae93
1 file changed
+121
+121
@@ -0,0 +1,121 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
pub const BlockKey = struct {
|
||||||
|
fileId: u64,
|
||||||
|
offset: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CacheStats = struct {
|
||||||
|
hits: u64 = 0,
|
||||||
|
misses: u64 = 0,
|
||||||
|
|
||||||
|
pub fn hitRate(self: CacheStats) f64 {
|
||||||
|
const total = self.hits + self.misses;
|
||||||
|
if (total == 0) return 0;
|
||||||
|
return @as(f64, @floatFromInt(self.hits)) / @as(f64, @floatFromInt(total));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Node = struct {
|
||||||
|
key: BlockKey,
|
||||||
|
bytes: []u8,
|
||||||
|
prev: ?*Node,
|
||||||
|
next: ?*Node,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BlockCache = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
capacityBytes: usize,
|
||||||
|
currentBytes: usize,
|
||||||
|
map: std.AutoHashMap(BlockKey, *Node),
|
||||||
|
head: ?*Node,
|
||||||
|
tail: ?*Node,
|
||||||
|
stats: CacheStats,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, capacityBytes: usize) BlockCache {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.capacityBytes = capacityBytes,
|
||||||
|
.currentBytes = 0,
|
||||||
|
.map = std.AutoHashMap(BlockKey, *Node).init(allocator),
|
||||||
|
.head = null,
|
||||||
|
.tail = null,
|
||||||
|
.stats = .{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *BlockCache) void {
|
||||||
|
var node = self.head;
|
||||||
|
while (node) |current| {
|
||||||
|
const nextNode = current.next;
|
||||||
|
self.allocator.free(current.bytes);
|
||||||
|
self.allocator.destroy(current);
|
||||||
|
node = nextNode;
|
||||||
|
}
|
||||||
|
self.map.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *BlockCache, key: BlockKey) ?[]const u8 {
|
||||||
|
if (self.map.get(key)) |node| {
|
||||||
|
self.detach(node);
|
||||||
|
self.pushFront(node);
|
||||||
|
self.stats.hits += 1;
|
||||||
|
return node.bytes;
|
||||||
|
}
|
||||||
|
self.stats.misses += 1;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put(self: *BlockCache, key: BlockKey, bytes: []const u8) !void {
|
||||||
|
if (self.map.get(key)) |node| {
|
||||||
|
self.currentBytes = self.currentBytes - node.bytes.len + bytes.len;
|
||||||
|
self.allocator.free(node.bytes);
|
||||||
|
node.bytes = try self.allocator.dupe(u8, bytes);
|
||||||
|
self.detach(node);
|
||||||
|
self.pushFront(node);
|
||||||
|
try self.evict();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const node = try self.allocator.create(Node);
|
||||||
|
errdefer self.allocator.destroy(node);
|
||||||
|
node.* = .{
|
||||||
|
.key = key,
|
||||||
|
.bytes = try self.allocator.dupe(u8, bytes),
|
||||||
|
.prev = null,
|
||||||
|
.next = null,
|
||||||
|
};
|
||||||
|
errdefer self.allocator.free(node.bytes);
|
||||||
|
try self.map.put(key, node);
|
||||||
|
self.currentBytes += node.bytes.len;
|
||||||
|
self.pushFront(node);
|
||||||
|
try self.evict();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evict(self: *BlockCache) !void {
|
||||||
|
while (self.currentBytes > self.capacityBytes and self.map.count() > 1) {
|
||||||
|
const victim = self.tail orelse break;
|
||||||
|
self.detach(victim);
|
||||||
|
_ = self.map.remove(victim.key);
|
||||||
|
self.currentBytes -= victim.bytes.len;
|
||||||
|
self.allocator.free(victim.bytes);
|
||||||
|
self.allocator.destroy(victim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach(self: *BlockCache, node: *Node) void {
|
||||||
|
if (node.prev) |p| p.next = node.next else self.head = node.next;
|
||||||
|
if (node.next) |n| n.prev = node.prev else self.tail = node.prev;
|
||||||
|
node.prev = null;
|
||||||
|
node.next = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pushFront(self: *BlockCache, node: *Node) void {
|
||||||
|
node.prev = null;
|
||||||
|
node.next = self.head;
|
||||||
|
if (self.head) |h| h.prev = node;
|
||||||
|
self.head = node;
|
||||||
|
if (self.tail == null) self.tail = node;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in new issue
Block a user