ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
cache_bench_report.c
Go to the documentation of this file.
1
24#include <string.h>
25
26#include "cache_bench.h"
27#include "cache_bench_host.h"
28#include "cache_bench_io.h"
29#include "ra8_attributes.h"
30#include "sweep_block.h"
31#include "trace.h"
32
41typedef enum : uint32_t {
49
51typedef enum : uint32_t {
52 k_cb_mid_cap = 256U,
54
56typedef enum : uint32_t {
59
61static const double s_cb_pct_scale_f = 100.0;
62
64static const uint32_t s_cb_sizes[] = {
65 32U,
66 (uint32_t)k_cb_size_64,
67 (uint32_t)k_cb_size_128,
68 (uint32_t)k_cb_size_256,
69 (uint32_t)k_cb_size_512,
70 (uint32_t)k_cb_size_1024,
71 (uint32_t)k_cb_size_2048,
72};
73
93static int internal_report_trace_header(const cb_trace_t* tr, cb_sink_t* sink, uint32_t nsz)
94{
95 if (cb_sink_format(sink,
96 "\n### %s (%llu accesses, footprint %u pages)\n\n",
97 tr->name,
98 (unsigned long long)tr->n,
99 tr->footprint) != k_cb_io_ok ||
100 cb_sink_format(sink, "| policy |") != k_cb_io_ok) {
101 return 1;
102 }
103 for (uint32_t s = 0U; s < nsz; ++s) {
104 if (cb_sink_format(sink, " %u |", s_cb_sizes[s]) != k_cb_io_ok) {
105 return 1;
106 }
107 }
108 if (cb_sink_format(sink, "\n|--------|") != k_cb_io_ok) {
109 return 1;
110 }
111 for (uint32_t s = 0U; s < nsz; ++s) {
112 if (cb_sink_format(sink, "------|") != k_cb_io_ok) {
113 return 1;
114 }
115 }
116 return cb_sink_format(sink, "\n") != k_cb_io_ok ? 1 : 0;
117}
118
141 const cb_trace_t* tr,
142 cb_workspace_t* workspace,
143 cb_sink_t* sink,
144 uint32_t nsz)
145{
146 if (cb_sink_format(sink, "| %-14s |", policy->name) != k_cb_io_ok) {
147 return 1;
148 }
149 for (uint32_t s = 0U; s < nsz; ++s) {
150 cb_result_t r = {};
151 if (cb_replay(policy, tr, s_cb_sizes[s], workspace, &r) != 0) {
152 return 1;
153 }
154 const double hit =
155 (r.accesses == 0U) ? 0.0 : (s_cb_pct_scale_f * (double)r.hits / (double)r.accesses);
156 if (cb_sink_format(sink, " %5.1f |", hit) != k_cb_io_ok) {
157 return 1;
158 }
159 }
160 return cb_sink_format(sink, "\n") != k_cb_io_ok ? 1 : 0;
161}
162
183static int internal_report_trace(const cb_trace_t* tr, cb_workspace_t* workspace, cb_sink_t* sink)
184{
185 const uint32_t nsz = (uint32_t)(sizeof(s_cb_sizes) / sizeof(s_cb_sizes[0]));
186 if (internal_report_trace_header(tr, sink, nsz) != 0) {
187 return 1;
188 }
189 for (uint32_t p = 0U; p < g_cb_policy_count; ++p) {
190 if (internal_report_trace_row(g_cb_policies[p], tr, workspace, sink, nsz) != 0) {
191 return 1;
192 }
193 }
194 return 0;
195}
196
218static int internal_report_summary(const cb_trace_t* traces,
219 uint32_t ntr,
220 cb_workspace_t* workspace,
221 cb_sink_t* sink)
222{
223 const uint32_t mid_cap = (uint32_t)k_cb_mid_cap;
224 if (cb_sink_format(sink, "\n## Summary at %u frames (mean over all workloads)\n\n", mid_cap) !=
225 k_cb_io_ok ||
226 cb_sink_format(sink, "| policy | mean hit %% | worst scan/evict | meta bytes/frame |\n") !=
227 k_cb_io_ok ||
228 cb_sink_format(sink, "|--------|-----------:|-----------------:|-----------------:|\n") !=
229 k_cb_io_ok) {
230 return 1;
231 }
232 for (uint32_t p = 0U; p < g_cb_policy_count; ++p) {
233 double sum_hit = 0.0;
234 uint32_t worst = 0U;
235 for (uint32_t t = 0U; t < ntr; ++t) {
236 cb_result_t r = {};
237 if (cb_replay(g_cb_policies[p], &traces[t], mid_cap, workspace, &r) != 0) {
238 return 1;
239 }
240 sum_hit +=
241 (r.accesses == 0U) ? 0.0 : (s_cb_pct_scale_f * (double)r.hits / (double)r.accesses);
242 if (r.worst_scan > worst) {
243 worst = r.worst_scan;
244 }
245 }
246 if (cb_sink_format(sink,
247 "| %-14s | %10.2f | %16u | %16zu |\n",
248 g_cb_policies[p]->name,
249 sum_hit / (double)ntr,
250 worst,
251 g_cb_policies[p]->meta_bytes) != k_cb_io_ok) {
252 return 1;
253 }
254 }
255 return 0;
256}
257
259typedef enum : uint8_t {
263
264typedef enum : size_t {
267
269typedef struct {
270 uint64_t before;
271 alignas(max_align_t) uint8_t bytes[1048576U];
272 uint64_t after;
274
275typedef enum : uint64_t {
276 k_cb_sweep_canary_before = 0x0CACEB00C0FFEE11ULL,
277 k_cb_sweep_canary_after = 0xA11CE55E0BADC0DEULL,
279
301static uint32_t
302internal_load_argv_traces(int argc, char** argv, cb_trace_t* loaded, cb_host_source_t* sources)
303{
304 uint32_t nloaded = 0U;
305 for (int a = 1; (a < argc) && (nloaded < (uint32_t)k_cb_max_loaded); ++a) {
306 const size_t name_length = strcspn(argv[a], "=");
307 if ((argv[a][name_length] != '=') ||
308 (strncmp(argv[a], "--output=", (size_t)k_cb_output_prefix_bytes) == 0)) {
309 continue;
310 }
311 cb_source_t source = {};
312 if ((cb_host_source_open(&argv[a][name_length + 1U], &sources[nloaded], &source) ==
313 k_cb_io_ok) &&
314 (cb_trace_bind(&source, argv[a], name_length, &loaded[nloaded]) == k_cb_io_ok)) {
315 nloaded++;
316 } else if (sources[nloaded].fd >= 0) {
317 (void)cb_host_source_close(&sources[nloaded]);
318 } else {
319 /* The open bound no descriptor, so there is nothing to close here. */
320 }
321 }
322 return nloaded;
323}
324
342static const char* internal_output_path(int argc, char** argv)
343{
344 for (int i = 1; i < argc; ++i) {
345 if (strncmp(argv[i], "--output=", (size_t)k_cb_output_prefix_bytes) == 0) {
346 return &argv[i][k_cb_output_prefix_bytes];
347 }
348 }
349 return nullptr;
350}
351
369static int internal_run_sweep(cb_sink_t* output, cb_sink_t* error)
370{
371 cb_host_scratch_t scratch_binding = {.fd = -1};
372 cb_scratch_t scratch = {};
373 if (cb_host_scratch_open(&scratch_binding, &scratch) != k_cb_io_ok) {
374 return 1;
375 }
377 static cb_sweep_backing_t s_cb_sweep_backing;
378 s_cb_sweep_backing.before = (uint64_t)k_cb_sweep_canary_before;
379 s_cb_sweep_backing.after = (uint64_t)k_cb_sweep_canary_after;
380 cb_sweep_config_t config = {.cache_backing = s_cb_sweep_backing.bytes,
381 .cache_capacity = sizeof(s_cb_sweep_backing.bytes),
382 .workspace = s_cb_composition_workspace,
383 .workspace_capacity = sizeof(s_cb_composition_workspace),
384 .scratch = &scratch,
385 .output = output,
386 .error = error};
387 int result = cb_sweep_block(&config);
388 if ((s_cb_sweep_backing.before != (uint64_t)k_cb_sweep_canary_before) ||
389 (s_cb_sweep_backing.after != (uint64_t)k_cb_sweep_canary_after) ||
390 (cb_host_scratch_close(&scratch_binding) != k_cb_io_ok)) {
391 result = 1;
392 }
393 return result;
394}
395
413static int internal_close_sources(cb_host_source_t* sources, uint32_t count)
414{
415 int result = 0;
416 for (uint32_t index = 0U; index < count; ++index) {
417 if (cb_host_source_close(&sources[index]) != k_cb_io_ok) {
418 result = 1;
419 }
420 }
421 return result;
422}
423
443static int internal_run_capacity(int argc, char** argv, cb_sink_t* output, cb_sink_t* error)
444{
446 cb_traces_synthetic(traces);
447 cb_trace_t loaded[k_cb_max_loaded] = {};
448 cb_host_source_t sources[k_cb_max_loaded] = {};
449 for (uint32_t i = 0U; i < (uint32_t)k_cb_max_loaded; ++i) {
450 sources[i].fd = -1;
451 }
452 const uint32_t nloaded = internal_load_argv_traces(argc, argv, loaded, sources);
453 cb_workspace_t workspace = {.data = s_cb_composition_workspace,
454 .capacity = sizeof(s_cb_composition_workspace)};
455 const bool banner_ok =
456 (cb_sink_format(output, "# #147 eviction-policy benchmark\n") == k_cb_io_ok) &&
457 (cb_sink_format(output, "\nHit rate (%%) by cache size (frames). Higher is better.\n") ==
458 k_cb_io_ok);
459 int result = 1;
460 if (banner_ok) {
461 result = 0;
462 }
463 for (uint32_t t = 0U; (t < (uint32_t)k_cb_synthetic_trace_count) && (result == 0); ++t) {
464 result = internal_report_trace(&traces[t], &workspace, output);
465 }
466 for (uint32_t t = 0U; (t < nloaded) && (result == 0); ++t) {
467 result = internal_report_trace(&loaded[t], &workspace, output);
468 }
469 if (result == 0) {
470 result =
471 internal_report_summary(traces, (uint32_t)k_cb_synthetic_trace_count, &workspace, output);
472 }
473 result |= internal_close_sources(sources, nloaded);
474 if (result != 0) {
475 (void)cb_sink_format(error,
476 "cache_bench: run failed (workspace required=%zu supplied=%zu)\n",
477 workspace.required,
478 workspace.capacity);
479 }
480 return result;
481}
482
483int main(int argc, char** argv)
484{
485 cb_sink_t error = {};
486 cb_host_standard_sinks(nullptr, &error);
487 cb_host_output_t output_binding = {};
488 cb_sink_t output = {};
489 if (cb_host_output_open(internal_output_path(argc, argv), &output_binding, &output) !=
490 k_cb_io_ok) {
491 (void)cb_sink_format(&error, "cache_bench: output open failed\n");
492 return 1;
493 }
494 const int result = ((argc > 1) && (strcmp(argv[1], "--sweep-block") == 0))
495 ? internal_run_sweep(&output, &error)
496 : internal_run_capacity(argc, argv, &output, &error);
497 if ((result == 0) && (cb_host_output_commit(&output_binding) == k_cb_io_ok)) {
498 return 0;
499 }
500 cb_host_output_abort(&output_binding);
501 return 1;
502}
Eviction-policy comparison harness for the #147 memory-hierarchy decision record: the DIP seam every ...
const cache_policy_t *const g_cb_policies[]
The registered policy table (defined in src/policies.c).
Definition policies.c:586
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.
struct cb_trace cb_trace_t
Definition cache_bench.h:28
const uint32_t g_cb_policy_count
Number of entries in g_cb_policies.
Definition policies.c:594
POSIX raw-descriptor composition bindings for cache_bench.
cb_io_status_t cb_host_source_open(const char *path, cb_host_source_t *binding, cb_source_t *source)
Open a captured-trace path and publish its source seam.
cb_io_status_t cb_host_scratch_close(cb_host_scratch_t *binding)
Close the scratch transaction.
cb_io_status_t cb_host_scratch_open(cb_host_scratch_t *binding, cb_scratch_t *scratch)
Create an unlinked raw-descriptor scratch transaction.
void cb_host_output_abort(cb_host_output_t *binding)
Abandon a temporary sibling while preserving the destination.
void cb_host_standard_sinks(cb_sink_t *output, cb_sink_t *error)
Bind the process standard-output and standard-error descriptor sinks.
cb_io_status_t cb_host_output_open(const char *path, cb_host_output_t *binding, cb_sink_t *sink)
Open a sibling output transaction, or bind descriptor one for NULL.
cb_io_status_t cb_host_output_commit(cb_host_output_t *binding)
Flush and atomically publish a transactional output.
cb_io_status_t cb_host_source_close(cb_host_source_t *binding)
Close a source binding.
Bounded byte-source and text-sink seams for cache_bench.
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.
static int internal_run_capacity(int argc, char **argv, cb_sink_t *output, cb_sink_t *error)
Execute the capacity report over synthetic and captured traces.
cb_composition_limit_t
@ k_cb_composition_workspace_bytes
Maximum exact metadata budget.
int main(int argc, char **argv)
static int internal_run_sweep(cb_sink_t *output, cb_sink_t *error)
Compose scratch, cache, and workspace bindings and execute block mode.
static int internal_report_summary(const cb_trace_t *traces, uint32_t ntr, cb_workspace_t *workspace, cb_sink_t *sink)
Print the cross-workload summary (WCET + metadata + mean hit rate).
static int internal_report_trace_row(const cache_policy_t *policy, const cb_trace_t *tr, cb_workspace_t *workspace, cb_sink_t *sink, uint32_t nsz)
Write one policy's hit-rate row across every swept cache size.
cb_sweep_canary_t
@ k_cb_sweep_canary_after
Trailing guard value.
@ k_cb_sweep_canary_before
Leading guard value.
static uint32_t internal_load_argv_traces(int argc, char **argv, cb_trace_t *loaded, cb_host_source_t *sources)
Load the extra captured traces named on the command line.
static int internal_close_sources(cb_host_source_t *sources, uint32_t count)
Close every captured source, preserving any close failure.
cb_loaded_cap_t
Capacity of the extra captured-trace table filled from argv.
@ k_cb_max_loaded
Most <name>=<path> traces accepted per run.
@ k_cb_output_prefix_bytes
Bytes in the literal --output= prefix.
cb_bench_size_t
Swept cache capacities (in frames) used on the RAM-budget axis.
@ k_cb_size_64
Smallest evaluated capacity (frames).
@ k_cb_size_2048
Largest evaluated capacity (frames).
@ k_cb_size_512
512-frame sweep point.
@ k_cb_size_128
128-frame sweep point.
@ k_cb_size_1024
1 K-frame sweep point.
@ k_cb_size_256
Mid-budget representative sweep point.
cb_mid_cap_t
Representative mid-budget capacity used in the summary table.
@ k_cb_mid_cap
Mid-point capacity (frames) for the summary view.
static const uint32_t s_cb_sizes[]
Swept cache capacities (frames) – the RAM-budget axis.
cb_pct_scale_t
Full scale used when computing a hit-rate percentage (integer form).
@ k_cb_pct_scale
Divisor to convert a ratio to a percentage.
static const char * internal_output_path(int argc, char **argv)
Find the optional report destination in the argument vector.
static int internal_report_trace(const cb_trace_t *tr, cb_workspace_t *workspace, cb_sink_t *sink)
Print the per-trace hit-rate matrix (policies x cache sizes).
static uint8_t s_cb_composition_workspace[k_cb_composition_workspace_bytes]
static const double s_cb_pct_scale_f
Floating-point 100.0 scale factor for hit-rate percentage output.
static int internal_report_trace_header(const cb_trace_t *tr, cb_sink_t *sink, uint32_t nsz)
Write the per-trace table header: title line, column heads, rule row.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
The replacement-policy DIP seam (the eventual firmware Layer-2 seam).
Definition cache_bench.h:75
const char * name
Policy name for the report table.
Definition cache_bench.h:76
Output transaction or borrowed standard descriptor.
Unlinked raw-descriptor scratch binding.
Borrowed raw descriptor source binding.
int fd
Open read-only descriptor.
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 accesses
Total accesses replayed.
Definition cache_bench.h:98
Injected bounded random-access scratch transaction.
Injected output sink.
Immutable injected byte source with a snapshotted length.
uint64_t before
Detect setup underflow.
uint64_t after
Detect setup overflow.
uint8_t bytes[1048576U]
Semantic cache budget.
Caller-owned bindings for the block sweep.
Definition sweep_block.h:51
const char * name
Stable display name.
Definition trace.h:43
uint32_t footprint
Reported working-set pages.
Definition trace.h:45
uint64_t n
Valid access count.
Definition trace.h:44
Caller-owned replay workspace and exact capacity diagnostics.
Definition cache_bench.h:59
size_t required
Exact bytes required by the latest request.
Definition cache_bench.h:62
size_t capacity
Supplied bytes.
Definition cache_bench.h:61
#208 block/frame-size sweep: the byte-size axis the capacity sweep never touches, so the chunked ....
int cb_sweep_block(cb_sweep_config_t *config)
Run the #208 block/frame-size sweep and print the report.
Resettable, allocation-free access streams for cache_bench.
@ k_cb_synthetic_trace_count
Fixed corpus size.
Definition trace.h:22
cb_io_status_t cb_trace_bind(const cb_source_t *source, const char *name, size_t name_length, cb_trace_t *out)
Validate and bind one captured decimal <object> <page> source.
Definition trace.c:473
void cb_traces_synthetic(cb_trace_t out[k_cb_synthetic_trace_count])
Populate the fixed built-in corpus without acquiring storage.
Definition trace.c:141