ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
cache_bench.c
Go to the documentation of this file.
1
24#include "cache_bench.h"
25
26#include <string.h>
27
28#include "cache_bench_io.h"
29#include "ra8_attributes.h"
30#include "trace.h"
31
50static uint32_t internal_pow2_ceil(uint32_t v)
51{
52 uint32_t p = 1U;
53 while (p < v) {
54 p <<= 1U;
55 }
56 return p;
57}
58
69typedef enum : uint64_t {
70 k_hash_mix_mul = 0xFF51AFD7ED558CCDULL,
72
81typedef enum : uint8_t {
84
103static uint32_t internal_hash(cb_key_t k)
104{
105 uint64_t h = ((uint64_t)k.object_id << 32U) | (uint64_t)k.page;
106 h ^= h >> (uint8_t)k_hash_shift;
107 h *= (uint64_t)k_hash_mix_mul;
108 h ^= h >> (uint8_t)k_hash_shift;
109 return (uint32_t)h;
110}
111
131{
132 return (a.object_id == b.object_id) && (a.page == b.page);
133}
134
143typedef struct {
144 int32_t* bucket;
145 int32_t* next;
146 uint32_t mask;
147} cb_index_t;
148
168static int32_t internal_index_find(const cb_index_t* idx, const cb_frame_t* frames, cb_key_t key)
169{
170 for (int32_t j = idx->bucket[internal_hash(key) & idx->mask]; j != -1; j = idx->next[j]) {
171 if (internal_key_eq(frames[j].key, key)) {
172 return j;
173 }
174 }
175 return -1;
176}
177
195static void internal_index_remove(cb_index_t* idx, const cb_frame_t* frames, uint32_t frame)
196{
197 const uint32_t ob = internal_hash(frames[frame].key) & idx->mask;
198 int32_t prev = -1;
199 for (int32_t j = idx->bucket[ob]; j != -1; prev = j, j = idx->next[j]) {
200 if (j == (int32_t)frame) {
201 if (prev == -1) {
202 idx->bucket[ob] = idx->next[j];
203 } else {
204 idx->next[prev] = idx->next[j];
205 }
206 break;
207 }
208 }
209}
210
227static void internal_index_push(cb_index_t* idx, uint32_t frame, cb_key_t key)
228{
229 const uint32_t b = internal_hash(key) & idx->mask;
230 idx->next[frame] = idx->bucket[b];
231 idx->bucket[b] = (int32_t)frame;
232}
233
256 cb_cache_t* cache,
257 cb_index_t* idx,
258 uint32_t* filled,
259 cb_result_t* out)
260{
261 if (*filled < cache->capacity) {
262 const uint32_t frame = *filled;
263 (*filled)++;
264 return frame;
265 }
266 uint32_t scanned = 0U;
267 const uint32_t frame = pol->pick_victim(cache, &scanned);
268 out->evictions++;
269 out->total_scan += scanned;
270 if (scanned > out->worst_scan) {
271 out->worst_scan = scanned;
272 }
273 internal_index_remove(idx, cache->frames, frame);
274 return frame;
275}
276
293static size_t internal_align_size(size_t value)
294{
295 const size_t alignment = alignof(max_align_t);
296 if (value > (SIZE_MAX - (alignment - 1U))) {
297 return 0U;
298 }
299 return (value + alignment - 1U) & ~(alignment - 1U);
300}
301
320static bool internal_size_multiply(size_t left, size_t right, size_t* result)
321{
322 if ((result == nullptr) || ((left != 0U) && (right > (SIZE_MAX / left)))) {
323 return false;
324 }
325 *result = left * right;
326 return true;
327}
328
329typedef enum : uint32_t {
330 k_cb_max_hash_capacity = UINT32_C(1) << 29U,
332
333size_t cb_replay_workspace_required(const cache_policy_t* pol, uint32_t capacity)
334{
335 if ((pol == nullptr) || (capacity == 0U) || (capacity > (uint32_t)k_cb_max_hash_capacity)) {
336 return 0U;
337 }
338 const uint32_t hsize = internal_pow2_ceil(capacity * 4U);
339 size_t frame_bytes;
340 size_t hash_bytes;
341 size_t link_bytes;
342 size_t policy_frame_bytes;
343 if (!internal_size_multiply(capacity, sizeof(cb_frame_t), &frame_bytes) ||
344 !internal_size_multiply(hsize, sizeof(int32_t), &hash_bytes) ||
345 !internal_size_multiply(capacity, sizeof(int32_t), &link_bytes) ||
346 !internal_size_multiply(capacity, pol->state_frame_bytes, &policy_frame_bytes) ||
347 (pol->state_base_bytes > (SIZE_MAX - policy_frame_bytes))) {
348 return 0U;
349 }
350 const size_t parts[] = {
351 frame_bytes,
352 hash_bytes,
353 link_bytes,
354 pol->state_base_bytes + policy_frame_bytes,
355 };
356 size_t total = 0U;
357 for (size_t i = 0U; i < (sizeof(parts) / sizeof(parts[0])); ++i) {
358 const size_t aligned = internal_align_size(parts[i]);
359 if ((aligned == 0U) || (total > (SIZE_MAX - aligned))) {
360 return 0U;
361 }
362 total += aligned;
363 }
364 return total;
365}
366
368static void* internal_workspace_take(cb_workspace_t* workspace, size_t* used, size_t bytes)
369{
370 const size_t span = internal_align_size(bytes);
371 if ((span == 0U) || (*used > workspace->capacity) || (span > (workspace->capacity - *used))) {
372 return nullptr;
373 }
374 void* result = &workspace->data[*used];
375 *used += span;
376 return result;
377}
378
402 cb_frame_t** frames,
403 void** policy_data,
404 const cache_policy_t* pol,
405 uint32_t capacity,
406 cb_workspace_t* workspace)
407{
408 const size_t required = cb_replay_workspace_required(pol, capacity);
409 workspace->required = required;
410 if ((required == 0U) || (workspace->data == nullptr) ||
411 (((uintptr_t)workspace->data % alignof(max_align_t)) != 0U) ||
412 (workspace->capacity < required)) {
413 return false;
414 }
415 size_t used = 0U;
416 const uint32_t hsize = internal_pow2_ceil(capacity * 4U);
417 *frames =
418 (cb_frame_t*)internal_workspace_take(workspace, &used, (size_t)capacity * sizeof(cb_frame_t));
419 idx->bucket =
420 (int32_t*)internal_workspace_take(workspace, &used, (size_t)hsize * sizeof(int32_t));
421 idx->next =
422 (int32_t*)internal_workspace_take(workspace, &used, (size_t)capacity * sizeof(int32_t));
423 *policy_data =
424 internal_workspace_take(workspace,
425 &used,
426 pol->state_base_bytes + ((size_t)capacity * pol->state_frame_bytes));
427 idx->mask = hsize - 1U;
428 if ((*frames == nullptr) || (idx->bucket == nullptr) || (idx->next == nullptr) ||
429 (*policy_data == nullptr)) {
430 return false;
431 }
432 memset(*frames, 0, (size_t)capacity * sizeof(cb_frame_t));
433 memset(*policy_data, 0, pol->state_base_bytes + ((size_t)capacity * pol->state_frame_bytes));
434 for (uint32_t i = 0U; i < hsize; ++i) {
435 idx->bucket[i] = -1;
436 }
437 if (used > workspace->high_water) {
438 workspace->high_water = used;
439 }
440 return true;
441}
442
458{
459 (void)frames;
460 *idx = (cb_index_t){};
461}
462
484 const cb_trace_t* trace,
485 cb_cache_t* cache,
486 cb_index_t* index,
487 cb_result_t* out)
488{
489 cb_trace_cursor_t cursor = {};
490 if (cb_trace_cursor_open(trace, &cursor) != k_cb_io_ok) {
491 return 1;
492 }
493 uint32_t filled = 0U;
494 bool done = false;
495 while (!done) {
496 cb_key_t key = {};
497 if (cb_trace_cursor_next(&cursor, &key, &done) != k_cb_io_ok) {
498 return 1;
499 }
500 if (done) {
501 break;
502 }
503 const int32_t found = internal_index_find(index, cache->frames, key);
504 out->accesses++;
505 if (found != -1) {
506 out->hits++;
507 if (pol->on_access != nullptr) {
508 pol->on_access(cache, (uint32_t)found);
509 }
510 continue;
511 }
512 const uint32_t frame = internal_replay_take_frame(pol, cache, index, &filled, out);
513 cache->frames[frame].key = key;
514 cache->frames[frame].live = true;
515 internal_index_push(index, frame, key);
516 if (pol->on_insert != nullptr) {
517 pol->on_insert(cache, frame);
518 }
519 }
520 return (cb_trace_cursor_finish(&cursor) == k_cb_io_ok) ? 0 : 1;
521}
522
524 const cb_trace_t* trace,
525 uint32_t capacity,
526 cb_workspace_t* workspace,
527 cb_result_t* out)
528{
529 if (out != nullptr) {
530 *out = (cb_result_t){};
531 }
532 if ((pol == nullptr) || (trace == nullptr) || (capacity == 0U) || (workspace == nullptr) ||
533 (out == nullptr)) {
534 return 1;
535 }
536
537 cb_frame_t* frames = nullptr;
538 cb_index_t idx = {};
539 void* policy_data = nullptr;
540 const bool ready = internal_replay_open(&idx, &frames, &policy_data, pol, capacity, workspace);
541 cb_cache_t cache = {.frames = frames,
542 .capacity = capacity,
543 .policy_data = nullptr,
544 .policy_workspace = policy_data,
545 .policy_workspace_bytes =
546 pol->state_base_bytes + ((size_t)capacity * pol->state_frame_bytes)};
547 if (!ready || ((pol->init != nullptr) && (pol->init(&cache) != 0))) {
548 internal_replay_close(&idx, frames);
549 return 1;
550 }
551
552 const int result = internal_replay_stream(pol, trace, &cache, &idx, out);
553 if (pol->deinit != nullptr) {
554 pol->deinit(&cache);
555 }
556 internal_replay_close(&idx, frames);
557 return result;
558}
int cb_replay(const cache_policy_t *pol, const cb_trace_t *trace, uint32_t capacity, cb_workspace_t *workspace, cb_result_t *out)
Replay an access trace through one policy at a fixed capacity.
cb_hash_shift_t
Murmur3 finalizer bit-shift amounts used in internal_hash.
Definition cache_bench.c:81
@ k_hash_shift
Murmur3 64-bit finalizer shift.
Definition cache_bench.c:82
static void * internal_workspace_take(cb_workspace_t *workspace, size_t *used, size_t bytes)
static bool internal_size_multiply(size_t left, size_t right, size_t *result)
Multiply byte factors without overflowing size_t.
static void internal_index_remove(cb_index_t *idx, const cb_frame_t *frames, uint32_t frame)
Unlink frame's current key from its bucket chain before eviction.
size_t cb_replay_workspace_required(const cache_policy_t *pol, uint32_t capacity)
Return the exact caller workspace required by one replay.
static uint32_t internal_replay_take_frame(const cache_policy_t *pol, cb_cache_t *cache, cb_index_t *idx, uint32_t *filled, cb_result_t *out)
Pick the frame that receives key: a free frame while the cache fills, else the policy's victim (accou...
static uint32_t internal_pow2_ceil(uint32_t v)
Round v up to a power of two (>= 1).
Definition cache_bench.c:50
static uint32_t internal_hash(cb_key_t k)
Mix an (object,page) key into a 32-bit hash for bucket selection.
cb_hash_limit_t
@ k_cb_max_hash_capacity
Largest safe fourfold hash input.
static bool internal_key_eq(cb_key_t a, cb_key_t b)
Report whether two cache keys name the same (object, page).
static bool internal_replay_open(cb_index_t *idx, cb_frame_t **frames, void **policy_data, const cache_policy_t *pol, uint32_t capacity, cb_workspace_t *workspace)
Carve and initialize one replay's exact caller-owned storage.
static int32_t internal_index_find(const cb_index_t *idx, const cb_frame_t *frames, cb_key_t key)
Find the resident frame currently holding key, or -1.
cb_hash_mul_t
Murmur3 finalizer constants used in internal_hash.
Definition cache_bench.c:69
@ k_hash_mix_mul
Murmur3 64-bit finalizer multiplier.
Definition cache_bench.c:70
static void internal_replay_close(cb_index_t *idx, cb_frame_t *frames)
End the borrowed workspace bindings made by internal_replay_open.
static size_t internal_align_size(size_t value)
Round a byte count to the next maximum fundamental alignment.
static int internal_replay_stream(const cache_policy_t *pol, const cb_trace_t *trace, cb_cache_t *cache, cb_index_t *index, cb_result_t *out)
Drive one resettable trace through an already-bound cache and index.
static void internal_index_push(cb_index_t *idx, uint32_t frame, cb_key_t key)
Link frame into the bucket chain for key after a fresh insert.
Eviction-policy comparison harness for the #147 memory-hierarchy decision record: the DIP seam every ...
struct cb_trace cb_trace_t
Definition cache_bench.h:28
Bounded byte-source and text-sink seams for cache_bench.
@ k_cb_io_ok
Operation completed.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
static uint32_t internal_hash(const ra8_keycache_t *kc, const void *key)
Hash a key blob into a bucket index using the configured policy.
The replacement-policy DIP seam (the eventual firmware Layer-2 seam).
Definition cache_bench.h:75
void(* on_access)(cb_cache_t *c, uint32_t frame)
A resident key was just hit at frame (update recency/freq).
Definition cache_bench.h:85
void(* on_insert)(cb_cache_t *c, uint32_t frame)
frame was just (re)populated with a freshly-loaded key.
Definition cache_bench.h:87
void(* deinit)(cb_cache_t *c)
Release policy state.
Definition cache_bench.h:83
size_t state_base_bytes
Fixed policy workspace bytes.
Definition cache_bench.h:78
size_t state_frame_bytes
Additional bytes per frame.
Definition cache_bench.h:79
uint32_t(* pick_victim)(cb_cache_t *c, uint32_t *scanned)
Choose a live frame to evict.
Definition cache_bench.h:93
int(* init)(cb_cache_t *c)
Allocate + init policy state for a cb_cache_t.
Definition cache_bench.h:81
The fixed-capacity frame cache a policy manages.
Definition cache_bench.h:50
cb_frame_t * frames
capacity frame slots.
Definition cache_bench.h:51
One frame slot in the fixed page cache.
Definition cache_bench.h:37
cb_key_t key
Resident key, valid only when live is true.
Definition cache_bench.h:38
bool live
true: slot holds a resident page.
Definition cache_bench.h:39
Exact key->frame lookup over the resident set (chained hash).
uint32_t mask
Bucket-count-minus-one bit mask.
int32_t * bucket
Head frame index per bucket, or -1.
int32_t * next
Per-frame chain link, or -1.
A cache key: one (object, page) the reader's vm_get touches.
Definition cache_bench.h:31
uint32_t object_id
Opaque object handle (book / archive / font).
Definition cache_bench.h:32
uint32_t page
Page index within the object (offset / page-size).
Definition cache_bench.h:33
Per-(policy, trace, size) result row.
Definition cache_bench.h:97
uint64_t hits
Resident-set hits.
Definition cache_bench.h:99
uint32_t worst_scan
Max frames scanned in any eviction.
uint64_t evictions
pick_victim calls.
uint64_t total_scan
Sum of frames scanned (avg proxy).
uint64_t accesses
Total accesses replayed.
Definition cache_bench.h:98
Caller-owned state for one independent trace pass.
Definition trace.h:54
Caller-owned replay workspace and exact capacity diagnostics.
Definition cache_bench.h:59
uint8_t * data
Aligned writable storage.
Definition cache_bench.h:60
size_t required
Exact bytes required by the latest request.
Definition cache_bench.h:62
size_t high_water
Largest successfully provisioned request.
Definition cache_bench.h:63
size_t capacity
Supplied bytes.
Definition cache_bench.h:61
Resettable, allocation-free access streams for cache_bench.
cb_io_status_t cb_trace_cursor_next(cb_trace_cursor_t *cursor, cb_key_t *key, bool *done)
Emit the next key.
Definition trace.c:438
cb_io_status_t cb_trace_cursor_finish(const cb_trace_cursor_t *cursor)
Validate a captured pass against its bound count and fingerprint.
Definition trace.c:459
cb_io_status_t cb_trace_cursor_open(const cb_trace_t *trace, cb_trace_cursor_t *cursor)
Reset a cursor for an independent pass over a trace.
Definition trace.c:418