ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
sweep_block.c
Go to the documentation of this file.
1
21#include "sweep_block.h"
22
23#include <string.h>
24
25#include "miniz.h"
26#include "ra8_err.h"
27#include "ra8_vmem.h"
28#include "ra8_vsource.h"
30
39typedef enum : uint32_t {
47
49void ra8_log_emit_error(const char* tag, const char* message)
50{
51 (void)tag;
52 (void)message;
53}
54
56void ra8_log_emit_error_val(const char* tag, const char* message, uint32_t value)
57{
58 (void)tag;
59 (void)message;
60 (void)value;
61}
62
86static uint32_t internal_pow2_ceil(uint32_t v)
87{
88 uint32_t p = 1U;
89 while (p < v) {
90 p <<= 1U;
91 }
92 return p;
93}
94
95/* ----------------------------------------------------------- the sweep -- */
96
118
138{
139 if (c == nullptr) {
140 return;
141 }
142 *c = (cbs_cache_t){};
143}
144
160static size_t internal_align_size(size_t value)
161{
162 const size_t alignment = alignof(max_align_t);
163 return (value + alignment - 1U) & ~(alignment - 1U);
164}
165
183static void* internal_workspace_take(cb_sweep_config_t* config, size_t* used, size_t bytes)
184{
185 const size_t span = internal_align_size(bytes);
186 if ((*used > config->workspace_capacity) || (span > (config->workspace_capacity - *used))) {
187 return nullptr;
188 }
189 void* result = &config->workspace[*used];
190 *used += span;
191 return result;
192}
193
214 uint32_t block_bytes,
215 cb_sweep_config_t* config,
216 uint32_t* out_buckets)
217{
218 c->frames = (uint32_t)k_cbs_cache_bytes / block_bytes;
219 if (c->frames == 0U) {
220 c->frames = 1U;
221 }
222 uint32_t buckets = internal_pow2_ceil(c->frames * 2U);
223 if (buckets < (uint32_t)k_cbs_bucket_min) {
224 buckets = (uint32_t)k_cbs_bucket_min;
225 }
226 size_t used = config->workspace_used;
227 c->frame_mem = config->cache_backing;
228 c->meta =
230 &used,
231 (size_t)c->frames * sizeof(ra8_vmem_frame_t));
233 &used,
234 (size_t)c->frames * sizeof(ra8_vmem_key_t));
235 c->buckets = (int32_t*)internal_workspace_take(config, &used, (size_t)buckets * sizeof(int32_t));
236 config->workspace_required = used;
237 if ((c->meta == nullptr) || (c->keys == nullptr) || (c->buckets == nullptr)) {
238 return 1;
239 }
240 memset(config->cache_backing, 0, config->cache_capacity);
241 memset(c->meta, 0, (size_t)c->frames * sizeof(ra8_vmem_frame_t));
242 memset(c->keys, 0, (size_t)c->frames * sizeof(ra8_vmem_key_t));
243 *out_buckets = buckets;
244 return 0;
245}
246
276 const cbs_backend_t* be,
277 uint32_t blob_bytes,
278 uint32_t block_bytes,
279 cb_sweep_config_t* config)
280{
281 if ((c == nullptr) || (be == nullptr) || (be->read == nullptr) || (block_bytes == 0U) ||
282 (config == nullptr) || (config->cache_backing == nullptr) ||
283 (config->cache_capacity != (size_t)k_cbs_cache_bytes)) {
284 return 1;
285 }
286 *c = (cbs_cache_t){};
287 uint32_t nbuckets = 0U;
288 if (internal_cache_storage(c, block_bytes, config, &nbuckets) != 0) {
290 return 1;
291 }
292 c->meter = (cbs_meter_t){.inner = be->read, .inner_ctx = be->read_ctx};
293 if (ra8_vsource_init(&c->vs, c->objs, 1U) != k_ra8_ok) {
295 return 1;
296 }
297 if (ra8_vsource_add_paged(&c->vs, priv_meter_read, &c->meter, 0U, blob_bytes, &c->object_id) !=
298 k_ra8_ok) {
300 return 1;
301 }
302 const ra8_vmem_cfg_t cfg = {.frame_mem = c->frame_mem,
303 .frame_bytes = block_bytes,
304 .frame_count = c->frames,
305 .meta = c->meta,
306 .keys = c->keys,
307 .buckets = c->buckets,
308 .bucket_count = nbuckets,
309 .loader = ra8_vsource_loader,
310 .loader_ctx = &c->vs};
311 if (ra8_vmem_init(&c->vm, &cfg) != k_ra8_ok) {
313 return 1;
314 }
315 return 0;
316}
317
350 ra8_vsource_read_fn payload_read,
351 void* payload_ctx,
352 uint64_t n_reads,
353 uint64_t wrap_bytes,
354 cbs_row_t* row)
355{
356 if ((c == nullptr) || (payload_read == nullptr) || (row == nullptr) || (wrap_bytes == 0U)) {
357 return 1;
358 }
359 const uint64_t req = (uint64_t)k_cbs_req_bytes;
360 const uint64_t block = (uint64_t)c->vm.cfg.frame_bytes;
361 uint64_t bad = 0U;
362 const uint64_t t0 = priv_now_ns();
363 for (uint64_t i = 0U; i < n_reads; ++i) {
364 const uint64_t off = (i * req) % wrap_bytes;
365 void* page = nullptr;
366 if (ra8_vmem_get(&c->vm, c->object_id, off, &page) != k_ra8_ok) {
367 bad++;
368 continue;
369 }
370 const uint8_t* piece = &((const uint8_t*)page)[off % block];
371 uint8_t expected[k_cbs_req_bytes];
372 if ((payload_read(payload_ctx, off, expected, (uint32_t)req) != k_ra8_ok) ||
373 (memcmp(piece, expected, (size_t)req) != 0)) {
374 bad++;
375 }
376 if (ra8_vmem_put(&c->vm, page) != k_ra8_ok) {
377 bad++;
378 }
379 }
380 row->wall_ns = priv_now_ns() - t0;
381 row->reads = n_reads;
382 uint32_t hits = 0U;
383 uint32_t miss = 0U;
384 uint32_t evic = 0U;
385 if (ra8_vmem_stats(&c->vm, &hits, &miss, &evic) != k_ra8_ok) {
386 return 1;
387 }
388 row->hits = (uint64_t)hits;
389 row->misses = (uint64_t)miss;
390 row->evictions = (uint64_t)evic;
391 if (bad != 0U) {
392 return 1;
393 }
394 return 0;
395}
396
398typedef enum : uint8_t {
402} cbs_leg_t;
403
411static const char* const s_cbs_leg_names[k_cbs_leg_count] = {"seq", "hot"};
412
443 ra8_vsource_read_fn payload_read,
444 void* payload_ctx,
445 uint32_t block_bytes,
446 cbs_row_t* rows,
447 uint32_t* nrows,
448 cb_sweep_config_t* config)
449{
450 if ((be == nullptr) || (payload_read == nullptr) || (rows == nullptr) || (nrows == nullptr) ||
451 (config == nullptr)) {
452 return 1;
453 }
454 config->workspace_used = config->workspace_floor;
455 if (be->setup(be, payload_read, payload_ctx, (uint32_t)k_cbs_blob_bytes, block_bytes, config) !=
456 0) {
457 return 1;
458 }
459 int rc = 0;
460 for (uint8_t leg = 0U; leg < (uint8_t)k_cbs_leg_count; ++leg) {
461 cbs_cache_t c = {};
462 if (internal_cache_open(&c, be, (uint32_t)k_cbs_blob_bytes, block_bytes, config) != 0) {
463 rc = 1;
464 break;
465 }
466 const uint64_t src0 = (be->src_bytes != nullptr) ? *be->src_bytes : 0U;
467 const bool seq = (leg == (uint8_t)k_cbs_leg_seq);
468 const uint64_t n =
469 seq ? ((uint64_t)k_cbs_seq_passes * (uint64_t)k_cbs_blob_bytes / (uint64_t)k_cbs_req_bytes)
470 : (uint64_t)k_cbs_hot_reads;
471 const uint64_t wrap = seq ? (uint64_t)k_cbs_blob_bytes : (uint64_t)block_bytes;
472 cbs_row_t* row = &rows[*nrows];
473 *row = (cbs_row_t){.backend = be->name,
474 .leg = s_cbs_leg_names[leg],
475 .block_bytes = block_bytes,
476 .frames = c.frames,
477 .backing_bytes = be->backing_bytes};
478 const int drc = internal_drive(&c, payload_read, payload_ctx, n, wrap, row);
479 row->be_calls = c.meter.calls;
480 row->be_bytes = c.meter.bytes;
481 row->src_bytes = (be->src_bytes != nullptr) ? (*be->src_bytes - src0) : c.meter.bytes;
483 if (drc != 0) {
484 rc = 1;
485 break;
486 }
487 (*nrows)++;
488 if (priv_print_row(config->output, row) != 0) {
489 rc = 1;
490 break;
491 }
492 }
493 be->teardown(be);
494 return rc;
495}
496
504static const uint32_t s_cbs_blocks[] = {
505 (uint32_t)k_cbs_block_512b,
506 (uint32_t)k_cbs_block_1kib,
507 (uint32_t)k_cbs_block_4kib,
508 (uint32_t)k_cbs_block_16kib,
509 (uint32_t)k_cbs_block_64kib,
510 (uint32_t)k_cbs_block_256kib,
511};
512
535 cbs_payload_t* payload,
536 cbs_backend_t* backends,
537 uint32_t backend_count,
538 cbs_row_t* rows,
539 uint32_t* row_count)
540{
541 const uint32_t block_count = (uint32_t)(sizeof(s_cbs_blocks) / sizeof(s_cbs_blocks[0]));
542 int result = 0;
543 for (uint32_t backend = 0U; (backend < backend_count) && (result == 0); ++backend) {
544 for (uint32_t size = 0U; (size < block_count) && (result == 0); ++size) {
545 result = internal_run_block(&backends[backend],
547 payload,
548 s_cbs_blocks[size],
549 rows,
550 row_count,
551 config);
552 }
553 }
554 return result;
555}
556
578 const cbs_backend_t* backends,
579 uint32_t backend_count,
580 const cbs_row_t* rows,
581 uint32_t row_count)
582{
583 const uint32_t block_count = (uint32_t)(sizeof(s_cbs_blocks) / sizeof(s_cbs_blocks[0]));
584 if (cb_sink_format(config->output,
585 "\n## Summary (payload 8 MiB, cache budget 1 MiB, %u B reader requests)\n",
586 (unsigned)k_cbs_req_bytes) != k_cb_io_ok) {
587 return 1;
588 }
589 for (uint32_t backend = 0U; backend < backend_count; ++backend) {
590 if ((priv_print_seq_table(config->output,
591 rows,
592 row_count,
593 backends[backend].name,
595 block_count) != 0) ||
597 rows,
598 row_count,
599 backends[backend].name,
601 block_count) != 0)) {
602 return 1;
603 }
604 }
605 return priv_print_crossover(config->output, rows, row_count, s_cbs_blocks, block_count);
606}
607
609{
610 if ((config == nullptr) || (config->workspace == nullptr) || (config->output == nullptr) ||
611 (config->error == nullptr) || (config->scratch == nullptr)) {
612 return 1;
613 }
614 config->cache_required = (size_t)k_cbs_cache_bytes;
615 const size_t payload_bytes = priv_payload_workspace_required();
616 config->workspace_required = payload_bytes;
617 cbs_payload_t payload = {};
618 if ((payload_bytes > config->workspace_capacity) ||
619 (priv_payload_init(&payload, config->workspace, config->workspace_capacity) != 0)) {
620 return 1;
621 }
622 config->workspace_floor = internal_align_size(payload_bytes);
623 config->workspace_used = config->workspace_floor;
624 if (cb_sink_format(config->output, "# #208 block/frame-size sweep\n\n") != k_cb_io_ok ||
625 cb_sink_format(config->output,
626 "payload=%u cache_budget=%u req=%u seq_passes=%u hot_reads=%u zlib_level=%d "
627 "cache=ra8_vmem(SLRU)\n\n",
628 (unsigned)k_cbs_blob_bytes,
629 (unsigned)k_cbs_cache_bytes,
630 (unsigned)k_cbs_req_bytes,
631 (unsigned)k_cbs_seq_passes,
632 (unsigned)k_cbs_hot_reads,
633 MZ_BEST_COMPRESSION) != k_cb_io_ok) {
634 return 1;
635 }
636
637 cbs_row_t rows[k_cbs_max_rows] = {};
638 uint32_t row_count = 0U;
639 uint32_t backend_count = 0U;
640 cbs_backend_t* backends = priv_backends(&backend_count);
641 int rc = internal_execute(config, &payload, backends, backend_count, rows, &row_count);
642 if (rc == 0) {
643 rc = internal_summary(config, backends, backend_count, rows, row_count);
644 }
645 if (rc != 0) {
646 (void)cb_sink_format(config->error,
647 "sweep-block: sweep aborted (cache required=%zu supplied=%zu; "
648 "workspace required=%zu supplied=%zu)\n",
649 config->cache_required,
650 config->cache_capacity,
651 config->workspace_required,
652 config->workspace_capacity);
653 }
654 return rc;
655}
cb_io_status_t cb_sink_format(cb_sink_t *sink, const char *format,...)
Format one bounded record and publish it atomically to the sink seam.
@ k_cb_io_ok
Operation completed.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
Byte-range page cache with SLRU eviction (Layer 2, #147).
ra8_err_t ra8_vmem_put(ra8_vmem_t *vm, void *page)
Release one pin on a frame previously returned by ra8_vmem_get.
Definition ra8_vmem.c:179
ra8_err_t ra8_vmem_get(ra8_vmem_t *vm, uint32_t object_id, uint64_t offset, void **out_page)
Get (and pin) the frame holding object object_id at offset.
Definition ra8_vmem.c:162
ra8_err_t ra8_vmem_init(ra8_vmem_t *vm, const ra8_vmem_cfg_t *cfg)
Initialise a page cache over caller-supplied storage.
Definition ra8_vmem.c:131
ra8_err_t ra8_vmem_stats(const ra8_vmem_t *vm, uint32_t *out_hits, uint32_t *out_misses, uint32_t *out_evictions)
Report the cache hit / miss / eviction counters.
Definition ra8_vmem.c:199
ra8_keycache_cell_t ra8_vmem_frame_t
Per-frame cache metadata (one caller-owned array entry per frame).
Definition ra8_vmem.h:122
Virtual-memory object sources – the page-cache storage seam (Layer 1, #147).
ra8_err_t ra8_vsource_loader(void *ctx, uint32_t object_id, uint64_t offset, uint8_t *frame, uint32_t frame_bytes)
Fill a page frame from an object – the ra8_vmem_loader_fn adapter.
Definition ra8_vsource.c:84
ra8_err_t ra8_vsource_add_paged(ra8_vsource_t *vs, ra8_vsource_read_fn read, void *ctx, uint64_t base, uint64_t size, uint32_t *out_id)
Register a storage-paged object; returns its object_id.
Definition ra8_vsource.c:42
ra8_err_t(* ra8_vsource_read_fn)(void *ctx, uint64_t offset, uint8_t *buf, uint32_t len)
Read len bytes at offset from a paged object's backing.
Definition ra8_vsource.h:79
ra8_err_t ra8_vsource_init(ra8_vsource_t *vs, ra8_vsource_obj_t *objs, uint32_t cap)
Initialise an empty source registry over a caller-owned object array.
Definition ra8_vsource.c:29
Caller-owned bindings for the block sweep.
Definition sweep_block.h:51
size_t cache_capacity
Supplied cache bytes.
Definition sweep_block.h:53
cb_sink_t * error
Diagnostic destination.
Definition sweep_block.h:62
size_t workspace_floor
Persistent payload-index prefix.
Definition sweep_block.h:58
cb_sink_t * output
Report destination.
Definition sweep_block.h:61
size_t workspace_capacity
Supplied workspace bytes.
Definition sweep_block.h:56
uint8_t * workspace
Small metadata/codec-input workspace.
Definition sweep_block.h:55
cb_scratch_t * scratch
Host-composed streamed RBKC transaction.
Definition sweep_block.h:60
size_t workspace_used
Current phase workspace usage.
Definition sweep_block.h:59
uint8_t * cache_backing
Exactly the measured resident-cache budget.
Definition sweep_block.h:52
size_t workspace_required
Exact latest requirement.
Definition sweep_block.h:57
size_t cache_required
Exact semantic cache requirement.
Definition sweep_block.h:54
One byte-addressed backing store the sweep reads through – the backend DIP seam the #208 hardware leg...
One ra8_vmem instance sized for a swept block size + its storage.
uint32_t object_id
Registered object id.
ra8_vsource_obj_t objs[1]
The single registered object's slot.
ra8_vmem_key_t * keys
Per-frame key-storage array.
ra8_vmem_t vm
The real SLRU page cache under test.
uint32_t frames
Frame count at this block size.
int32_t * buckets
Hash-bucket heads.
ra8_vsource_t vs
Object-source registry (one object).
uint8_t * frame_mem
frames * block page storage.
cbs_meter_t meter
Vsource-seam storage-command meter.
ra8_vmem_frame_t * meta
Per-frame metadata array.
Counting shim around an ra8_vsource_read_fn.
uint64_t bytes
Bytes served through the shim.
uint64_t calls
Read calls forwarded so far.
Resettable exact pseudo-text byte source with bounded checkpoints.
One measured (backend, leg, block size) result row.
uint64_t evictions
ra8_vmem evictions.
uint64_t reads
Reader requests issued.
uint64_t wall_ns
Wall-clock time of the timed loop, in ns.
uint64_t be_calls
Backend read calls (storage commands).
uint64_t be_bytes
Bytes delivered to the cache by the backend.
uint64_t misses
ra8_vmem misses.
uint64_t hits
ra8_vmem hits.
uint64_t src_bytes
Raw medium bytes moved (compressed for RBKC).
Caller-supplied storage + loader for ra8_vmem_init.
Definition ra8_vmem.h:151
uint32_t frame_bytes
Bytes per frame (page size, e.g.
Definition ra8_vmem.h:153
The (object_id, frame-aligned offset) key the page cache hashes on.
Definition ra8_vmem.h:136
Page-cache state (caller-owned; treat as private).
Definition ra8_vmem.h:179
ra8_vmem_cfg_t cfg
The configuration (copied at init).
Definition ra8_vmem.h:180
One registered object's backing (paged or XIP).
Definition ra8_vsource.h:90
Object-source registry (caller-owned; treat as private).
static const uint32_t s_cbs_blocks[]
The swept block sizes, ascending (drives loops + knee search).
int cb_sweep_block(cb_sweep_config_t *config)
Run the #208 block/frame-size sweep and print the report.
static int internal_summary(cb_sweep_config_t *config, const cbs_backend_t *backends, uint32_t backend_count, const cbs_row_t *rows, uint32_t row_count)
Publish all human summary tables and the crossover verdict.
static int internal_execute(cb_sweep_config_t *config, cbs_payload_t *payload, cbs_backend_t *backends, uint32_t backend_count, cbs_row_t *rows, uint32_t *row_count)
Execute every backend and block-size pair and append its result rows.
static int internal_cache_storage(cbs_cache_t *c, uint32_t block_bytes, cb_sweep_config_t *config, uint32_t *out_buckets)
Carve and clear exact cache metadata from caller-owned bindings.
void ra8_log_emit_error(const char *tag, const char *message)
Log backend stub so ra8_check's RA8_CHECK_* macros link host-side.
Definition sweep_block.c:49
static uint32_t internal_pow2_ceil(uint32_t v)
Round v up to a power of two (>= 1).
Definition sweep_block.c:86
static int internal_cache_open(cbs_cache_t *c, const cbs_backend_t *be, uint32_t blob_bytes, uint32_t block_bytes, cb_sweep_config_t *config)
Stand up a real ra8_vmem cache with frame_bytes == block_bytes.
static int internal_drive(cbs_cache_t *c, ra8_vsource_read_fn payload_read, void *payload_ctx, uint64_t n_reads, uint64_t wrap_bytes, cbs_row_t *row)
Drive one timed leg: n_reads requests of k_cbs_req_bytes each, at offsets (i * req) % wrap_bytes,...
static const char *const s_cbs_leg_names[k_cbs_leg_count]
Report names for the two workload legs, indexed by cbs_leg_t.
cbs_leg_t
Leg indices for internal_run_block's per-leg loop.
@ k_cbs_leg_count
Number of legs.
@ k_cbs_leg_hot
Same-block re-read loop.
@ k_cbs_leg_seq
Sequential whole-object scan.
static void internal_cache_close(cbs_cache_t *c)
End a cbs_cache_t's borrowed workspace bindings (idempotent).
static void * internal_workspace_take(cb_sweep_config_t *config, size_t *used, size_t bytes)
Take one aligned region from the caller-owned sweep workspace.
static size_t internal_align_size(size_t value)
Round a workspace span to maximum fundamental alignment.
void ra8_log_emit_error_val(const char *tag, const char *message, uint32_t value)
Valued log backend stub (present for the linker, as reader_vmem).
Definition sweep_block.c:56
cbs_block_size_t
The swept block / frame / chunk sizes, in bytes.
Definition sweep_block.c:39
@ k_cbs_block_256kib
256 KiB (frame RAM gets expensive).
Definition sweep_block.c:45
@ k_cbs_block_4kib
Classic VM page size.
Definition sweep_block.c:42
@ k_cbs_block_16kib
16 KiB.
Definition sweep_block.c:43
@ k_cbs_block_512b
One SD sector.
Definition sweep_block.c:40
@ k_cbs_block_64kib
The current .rabook chunk default.
Definition sweep_block.c:44
@ k_cbs_block_1kib
1 KiB.
Definition sweep_block.c:41
static int internal_run_block(cbs_backend_t *be, ra8_vsource_read_fn payload_read, void *payload_ctx, uint32_t block_bytes, cbs_row_t *rows, uint32_t *nrows, cb_sweep_config_t *config)
Run both legs of one (backend, block size) combination.
#208 block/frame-size sweep: the byte-size axis the capacity sweep never touches, so the chunked ....
Module-private seams shared by the #208 sweep translation units.
ra8_err_t priv_meter_read(void *ctx, uint64_t offset, uint8_t *buf, uint32_t len)
ra8_vsource_read_fn forwarding through a cbs_meter_t.
int priv_print_hot_table(cb_sink_t *sink, const cbs_row_t *rows, uint32_t nrows, const char *be, const uint32_t *blocks, uint32_t nblocks)
Print one backend's same-block re-read summary table (leg b).
@ k_cbs_hot_reads
Accesses in the same-block re-read leg.
@ k_cbs_bucket_min
Minimum hash-bucket count for tiny caches.
@ k_cbs_cache_bytes
1 MiB resident cache budget (constant).
@ k_cbs_seq_passes
Whole-object passes in the seq leg.
@ k_cbs_blob_bytes
8 MiB payload (2^23; all sizes divide it).
@ k_cbs_max_rows
2 backends x 6 sizes x 2 legs.
@ k_cbs_req_bytes
Reader request grain (divides every size).
int priv_payload_init(cbs_payload_t *payload, void *workspace, size_t capacity)
Build the payload source index into caller-owned storage.
int priv_print_crossover(cb_sink_t *sink, const cbs_row_t *rows, uint32_t nrows, const uint32_t *blocks, uint32_t nblocks)
Name the measured crossover and print the chunk-size recommendation.
int priv_print_row(cb_sink_t *sink, const cbs_row_t *r)
Print one machine-parseable result row (sweep-block key=value ...).
int priv_print_seq_table(cb_sink_t *sink, const cbs_row_t *rows, uint32_t nrows, const char *be, const uint32_t *blocks, uint32_t nblocks)
Print one backend's sequential-scan summary table (leg a).
ra8_err_t priv_payload_read(void *ctx, uint64_t offset, uint8_t *buffer, uint32_t length)
Read exact historical pseudo-text bytes at any bounded offset.
cbs_backend_t * priv_backends(uint32_t *out_count)
Expose the registered sweep backends (the seam the HW leg extends).
size_t priv_payload_workspace_required(void)
Return exact workspace bytes required by the pseudo-text source index.
uint64_t priv_now_ns(void)
Monotonic wall-clock in nanoseconds.