runners: emubd/kiwibd: Adopted emulated simtime API
This is based on some work in external benchmarks. What's worked well
there is emulating a global simtime based on per-byte estimates.
This moves the emulated simtime into emubd/kiwibd, and extends the idea
with both per-byte and per-op timing estimates for hopefully more
realistic results.
---
The problem is how NAND flash reads work.
Per-byte timing estimates are surprisingly accurate for NOR flash. There
is some overhead for sending the address, but it's mostly dominated by
bus cost (~20ns/B [1]).
NAND flash, on the otherhand, technically does support byte-level reads,
but first needs to read into 2KiB buffer. Surprisingly, these are pretty
close in cost (~19ns/B bus [2] vs ~12ns/B buffer [2]).
This close-ness makes modeling NAND flash difficult. If we set
read_size=1, we risk hiding the cost of small reads, which littlefs3 is
full of (rbyd lookups). If we set read_size=2048, we unfairly penalize
littlefs3 for the same reason.
---
The solution here is to expose both per-byte and per-op timing
estimates. This lets you model NAND reads using two data points:
^
| realtime --> ...............o
| : .....'''' :
| ...............:'''' ^ :
| :....''''' | :
| ..........:::::: simtime :
| .....:'''' :
|o....:::::.....: :
|: :
|: :
+:-----------------------------------------------------------:>
min read max read
Where:
bus_timing = 19ns
buffer_timing = 25us
buffer_size = 2KiB
erase_size = 128KiB
min_read = buffer_timing
max_read = (erase_size/buffer_size)*buffer_timing - buffer_timing
read_timing = min_read
readed_timing = ((max_read - min_read)/erase_size) + bus_timing
simtime = reads*read_timing + readed*readed_timing
(per-op) (per-byte)
This should correctly penalize small reads without complicating
emubd/kiwibd too much.
That's the idea anyways! It will take some use to understand if this is
a reasonable approach.
As a plus, this is a superset of the per-byte model, so both can be used
for realistic vs idealistic simulations (and to test the bus+buffer
model itself).
1: https://www.winbond.com/resource-files/W25Q256JV%20SPI%20RevQ%2002072025%20Plus.pdf
2: https://www.winbond.com/resource-files/W25N01GV%20Rev%20R%20070323.pdf
This commit is contained in:
+60
-15
@@ -436,9 +436,9 @@ FILE *bench_trace_file = NULL;
|
||||
uint32_t bench_trace_cycles = 0;
|
||||
uint64_t bench_trace_time = 0;
|
||||
uint64_t bench_trace_open_time = 0;
|
||||
lfs3_emubd_sleep_t bench_read_sleep = 0.0;
|
||||
lfs3_emubd_sleep_t bench_prog_sleep = 0.0;
|
||||
lfs3_emubd_sleep_t bench_erase_sleep = 0.0;
|
||||
lfs3_emubd_ns_t bench_read_sleep = 0.0;
|
||||
lfs3_emubd_ns_t bench_prog_sleep = 0.0;
|
||||
lfs3_emubd_ns_t bench_erase_sleep = 0.0;
|
||||
|
||||
// this determines both the backtrace buffer and the trace printf buffer, if
|
||||
// trace ends up interleaved or truncated this may need to be increased
|
||||
@@ -604,9 +604,13 @@ void bench_permutation(size_t i, uint32_t *buffer, size_t size) {
|
||||
typedef struct bench_record {
|
||||
const char *m;
|
||||
uintmax_t n;
|
||||
lfs3_emubd_io_t last_reads;
|
||||
lfs3_emubd_io_t last_progs;
|
||||
lfs3_emubd_io_t last_erases;
|
||||
lfs3_emubd_io_t last_readed;
|
||||
lfs3_emubd_io_t last_proged;
|
||||
lfs3_emubd_io_t last_progged;
|
||||
lfs3_emubd_io_t last_erased;
|
||||
lfs3_emubd_ns_t last_simtime;
|
||||
} bench_record_t;
|
||||
|
||||
static struct lfs3_cfg *bench_cfg = NULL;
|
||||
@@ -622,12 +626,20 @@ void bench_reset(struct lfs3_cfg *cfg) {
|
||||
void bench_start(const char *m, uintmax_t n) {
|
||||
// measure current read/prog/erase
|
||||
assert(bench_cfg);
|
||||
lfs3_emubd_sio_t reads = lfs3_emubd_reads(bench_cfg);
|
||||
assert(reads >= 0);
|
||||
lfs3_emubd_sio_t progs = lfs3_emubd_progs(bench_cfg);
|
||||
assert(progs >= 0);
|
||||
lfs3_emubd_sio_t erases = lfs3_emubd_erases(bench_cfg);
|
||||
assert(erases >= 0);
|
||||
lfs3_emubd_sio_t readed = lfs3_emubd_readed(bench_cfg);
|
||||
assert(readed >= 0);
|
||||
lfs3_emubd_sio_t proged = lfs3_emubd_proged(bench_cfg);
|
||||
assert(proged >= 0);
|
||||
lfs3_emubd_sio_t progged = lfs3_emubd_progged(bench_cfg);
|
||||
assert(progged >= 0);
|
||||
lfs3_emubd_sio_t erased = lfs3_emubd_erased(bench_cfg);
|
||||
assert(erased >= 0);
|
||||
// note this can error if no timings provided
|
||||
lfs3_emubd_sns_t simtime = lfs3_emubd_simtime(bench_cfg);
|
||||
|
||||
// allocate a new record
|
||||
bench_record_t *record = mappend(
|
||||
@@ -637,31 +649,64 @@ void bench_start(const char *m, uintmax_t n) {
|
||||
&bench_record_capacity);
|
||||
record->m = m;
|
||||
record->n = n;
|
||||
record->last_reads = reads;
|
||||
record->last_progs = progs;
|
||||
record->last_erases = erases;
|
||||
record->last_readed = readed;
|
||||
record->last_proged = proged;
|
||||
record->last_progged = progged;
|
||||
record->last_erased = erased;
|
||||
record->last_simtime = simtime;
|
||||
}
|
||||
|
||||
void bench_stop(const char *m) {
|
||||
// measure current read/prog/erase
|
||||
assert(bench_cfg);
|
||||
lfs3_emubd_sio_t reads = lfs3_emubd_reads(bench_cfg);
|
||||
assert(reads >= 0);
|
||||
lfs3_emubd_sio_t progs = lfs3_emubd_progs(bench_cfg);
|
||||
assert(progs >= 0);
|
||||
lfs3_emubd_sio_t erases = lfs3_emubd_erases(bench_cfg);
|
||||
assert(erases >= 0);
|
||||
lfs3_emubd_sio_t readed = lfs3_emubd_readed(bench_cfg);
|
||||
assert(readed >= 0);
|
||||
lfs3_emubd_sio_t proged = lfs3_emubd_proged(bench_cfg);
|
||||
assert(proged >= 0);
|
||||
lfs3_emubd_sio_t progged = lfs3_emubd_progged(bench_cfg);
|
||||
assert(progged >= 0);
|
||||
lfs3_emubd_sio_t erased = lfs3_emubd_erased(bench_cfg);
|
||||
assert(erased >= 0);
|
||||
// note this can error if no timings provided
|
||||
lfs3_emubd_sns_t simtime = lfs3_emubd_simtime(bench_cfg);
|
||||
|
||||
// find our record
|
||||
for (size_t i = 0; i < bench_record_count; i++) {
|
||||
if (strcmp(bench_records[i].m, m) == 0) {
|
||||
// print results
|
||||
printf("benched %s %jd %"PRIu64" %"PRIu64" %"PRIu64"\n",
|
||||
bench_records[i].m,
|
||||
bench_records[i].n,
|
||||
readed - bench_records[i].last_readed,
|
||||
proged - bench_records[i].last_proged,
|
||||
erased - bench_records[i].last_erased);
|
||||
if (simtime >= 0) {
|
||||
printf("benched %s %jd "
|
||||
"%"PRIu64" %"PRIu64" %"PRIu64" "
|
||||
"%"PRIu64" %"PRIu64" %"PRIu64" "
|
||||
"%"PRIu64"\n",
|
||||
bench_records[i].m,
|
||||
bench_records[i].n,
|
||||
reads - bench_records[i].last_reads,
|
||||
progs - bench_records[i].last_progs,
|
||||
erases - bench_records[i].last_erases,
|
||||
readed - bench_records[i].last_readed,
|
||||
progged - bench_records[i].last_progged,
|
||||
erased - bench_records[i].last_erased,
|
||||
simtime - bench_records[i].last_simtime);
|
||||
} else {
|
||||
printf("benched %s %jd "
|
||||
"%"PRIu64" %"PRIu64" %"PRIu64" "
|
||||
"%"PRIu64" %"PRIu64" %"PRIu64"\n",
|
||||
bench_records[i].m,
|
||||
bench_records[i].n,
|
||||
reads - bench_records[i].last_reads,
|
||||
progs - bench_records[i].last_progs,
|
||||
erases - bench_records[i].last_erases,
|
||||
readed - bench_records[i].last_readed,
|
||||
progged - bench_records[i].last_progged,
|
||||
erased - bench_records[i].last_erased);
|
||||
}
|
||||
|
||||
// remove our record
|
||||
memmove(&bench_records[i],
|
||||
|
||||
Reference in New Issue
Block a user