waltgrace commited on
Commit
1fa479d
·
verified ·
1 Parent(s): 715943d

Add src/llama-expert-cache.cpp

Browse files
Files changed (1) hide show
  1. src/llama-expert-cache.cpp +233 -0
src/llama-expert-cache.cpp ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "llama-expert-cache.h"
2
+
3
+ #include <cstdlib>
4
+ #include <cstring>
5
+ #include <cassert>
6
+ #include <algorithm>
7
+
8
+ #ifdef __APPLE__
9
+ #include <fcntl.h>
10
+ #include <unistd.h>
11
+ #endif
12
+
13
+ #ifdef __linux__
14
+ #include <fcntl.h>
15
+ #include <unistd.h>
16
+ #endif
17
+
18
+ #ifdef _WIN32
19
+ #include <io.h>
20
+ #include <windows.h>
21
+ #endif
22
+
23
+ // Page size for aligned allocation (matches Apple Silicon and most x86)
24
+ static constexpr size_t ALLOC_ALIGNMENT = 4096;
25
+
26
+ static size_t align_up(size_t val, size_t alignment) {
27
+ return (val + alignment - 1) & ~(alignment - 1);
28
+ }
29
+
30
+ llama_expert_cache::llama_expert_cache(size_t max_bytes)
31
+ : max_bytes_(max_bytes)
32
+ , used_bytes_(0)
33
+ , stats_{0, 0, 0, 0, max_bytes} {
34
+ }
35
+
36
+ llama_expert_cache::~llama_expert_cache() {
37
+ for (auto & [key, entry] : cache_) {
38
+ free_aligned(entry.data, entry.size_bytes);
39
+ }
40
+ cache_.clear();
41
+ lru_order_.clear();
42
+ used_bytes_ = 0;
43
+ }
44
+
45
+ void * llama_expert_cache::alloc_aligned(size_t size) {
46
+ size_t alloc_size = align_up(size, ALLOC_ALIGNMENT);
47
+ #ifdef _WIN32
48
+ void * ptr = _aligned_malloc(alloc_size, ALLOC_ALIGNMENT);
49
+ #else
50
+ void * ptr = nullptr;
51
+ int ret = posix_memalign(&ptr, ALLOC_ALIGNMENT, alloc_size);
52
+ if (ret != 0) {
53
+ ptr = nullptr;
54
+ }
55
+ #endif
56
+ return ptr;
57
+ }
58
+
59
+ void llama_expert_cache::free_aligned(void * ptr, size_t /*size*/) {
60
+ if (!ptr) return;
61
+ #ifdef _WIN32
62
+ _aligned_free(ptr);
63
+ #else
64
+ free(ptr);
65
+ #endif
66
+ }
67
+
68
+ void * llama_expert_cache::load_from_disk(const llama_expert_disk_info & info) {
69
+ void * buf = alloc_aligned(info.size_bytes);
70
+ if (!buf) return nullptr;
71
+
72
+ #ifdef _WIN32
73
+ // Windows: use _lseeki64 + _read or ReadFile
74
+ _lseeki64(info.fd, info.file_offset, SEEK_SET);
75
+ size_t remaining = info.size_bytes;
76
+ char * dst = (char *)buf;
77
+ while (remaining > 0) {
78
+ int chunk = (int)std::min(remaining, (size_t)INT_MAX);
79
+ int n = _read(info.fd, dst, chunk);
80
+ if (n <= 0) {
81
+ free_aligned(buf, info.size_bytes);
82
+ return nullptr;
83
+ }
84
+ dst += n;
85
+ remaining -= n;
86
+ }
87
+ #else
88
+ // POSIX: use pread for thread-safe positional read (no seek mutex needed)
89
+ size_t remaining = info.size_bytes;
90
+ char * dst = (char *)buf;
91
+ off_t offset = (off_t)info.file_offset;
92
+ while (remaining > 0) {
93
+ ssize_t n = pread(info.fd, dst, remaining, offset);
94
+ if (n <= 0) {
95
+ free_aligned(buf, info.size_bytes);
96
+ return nullptr;
97
+ }
98
+ dst += n;
99
+ offset += n;
100
+ remaining -= n;
101
+ }
102
+ #endif
103
+
104
+ return buf;
105
+ }
106
+
107
+ void llama_expert_cache::evict_until_free(size_t needed) {
108
+ while (used_bytes_ + needed > max_bytes_ && !lru_order_.empty()) {
109
+ // Evict least recently used (back of list)
110
+ auto evict_key = lru_order_.back();
111
+ lru_order_.pop_back();
112
+
113
+ auto it = cache_.find(evict_key);
114
+ if (it != cache_.end()) {
115
+ used_bytes_ -= it->second.size_bytes;
116
+ free_aligned(it->second.data, it->second.size_bytes);
117
+ cache_.erase(it);
118
+ stats_.evictions++;
119
+ }
120
+ }
121
+ }
122
+
123
+ void * llama_expert_cache::ensure(const llama_expert_key & key,
124
+ const llama_expert_disk_info & disk_info) {
125
+ std::lock_guard<std::mutex> lock(mutex_);
126
+
127
+ // Check cache
128
+ auto it = cache_.find(key);
129
+ if (it != cache_.end()) {
130
+ // Hit: move to front of LRU
131
+ stats_.hits++;
132
+ lru_order_.erase(it->second.lru_it);
133
+ lru_order_.push_front(key);
134
+ it->second.lru_it = lru_order_.begin();
135
+ return it->second.data;
136
+ }
137
+
138
+ // Miss: load from disk
139
+ stats_.misses++;
140
+
141
+ size_t alloc_size = align_up(disk_info.size_bytes, ALLOC_ALIGNMENT);
142
+
143
+ // Evict until we have space
144
+ evict_until_free(alloc_size);
145
+
146
+ // Load from disk (this does I/O while holding the lock —
147
+ // acceptable for now, can be optimized with async prefetch later)
148
+ void * data = load_from_disk(disk_info);
149
+ if (!data) {
150
+ return nullptr;
151
+ }
152
+
153
+ // Insert into cache
154
+ lru_order_.push_front(key);
155
+ llama_expert_entry entry;
156
+ entry.key = key;
157
+ entry.data = data;
158
+ entry.size_bytes = alloc_size;
159
+ entry.lru_it = lru_order_.begin();
160
+ cache_[key] = entry;
161
+ used_bytes_ += alloc_size;
162
+ stats_.bytes_used = used_bytes_;
163
+
164
+ return data;
165
+ }
166
+
167
+ std::pair<void *, bool> llama_expert_cache::get_or_alloc(
168
+ const llama_expert_key & key, size_t size_bytes) {
169
+ std::lock_guard<std::mutex> lock(mutex_);
170
+
171
+ // Check cache
172
+ auto it = cache_.find(key);
173
+ if (it != cache_.end()) {
174
+ stats_.hits++;
175
+ lru_order_.erase(it->second.lru_it);
176
+ lru_order_.push_front(key);
177
+ it->second.lru_it = lru_order_.begin();
178
+ return {it->second.data, true}; // hit
179
+ }
180
+
181
+ // Miss
182
+ stats_.misses++;
183
+
184
+ size_t alloc_size = align_up(size_bytes, ALLOC_ALIGNMENT);
185
+ evict_until_free(alloc_size);
186
+
187
+ void * data = alloc_aligned(alloc_size);
188
+ if (!data) {
189
+ return {nullptr, false};
190
+ }
191
+
192
+ lru_order_.push_front(key);
193
+ llama_expert_entry entry;
194
+ entry.key = key;
195
+ entry.data = data;
196
+ entry.size_bytes = alloc_size;
197
+ entry.lru_it = lru_order_.begin();
198
+ cache_[key] = entry;
199
+ used_bytes_ += alloc_size;
200
+ stats_.bytes_used = used_bytes_;
201
+
202
+ return {data, false}; // miss — caller must fill
203
+ }
204
+
205
+ void llama_expert_cache::touch(const llama_expert_key & key) {
206
+ std::lock_guard<std::mutex> lock(mutex_);
207
+ auto it = cache_.find(key);
208
+ if (it != cache_.end()) {
209
+ lru_order_.erase(it->second.lru_it);
210
+ lru_order_.push_front(key);
211
+ it->second.lru_it = lru_order_.begin();
212
+ }
213
+ }
214
+
215
+ bool llama_expert_cache::contains(const llama_expert_key & key) const {
216
+ std::lock_guard<std::mutex> lock(mutex_);
217
+ return cache_.find(key) != cache_.end();
218
+ }
219
+
220
+ llama_expert_cache_stats llama_expert_cache::get_stats() const {
221
+ std::lock_guard<std::mutex> lock(mutex_);
222
+ auto s = stats_;
223
+ s.bytes_used = used_bytes_;
224
+ s.bytes_capacity = max_bytes_;
225
+ return s;
226
+ }
227
+
228
+ void llama_expert_cache::reset_stats() {
229
+ std::lock_guard<std::mutex> lock(mutex_);
230
+ stats_.hits = 0;
231
+ stats_.misses = 0;
232
+ stats_.evictions = 0;
233
+ }