Files
littlefs/benches/bench_wt.toml
T
Christopher Haster 10e77d9177 runners: bench: Added BENCH_SIMTIME/SIMRESET/PAUSE/RESUME/etc
- BENCH_SIMTIME()   => lfs3_kiwibd_simtime()
- BENCH_SIMRESET()  => lfs3_kiwibd_simreset()
- BENCH_SIMPAUSE()  => lfs3_kiwibd_simpause()
- BENCH_SIMRESUME() => lfs3_kiwibd_simresume()
- BENCH_RESET()     => lfs3_kiwibd_simreset() + BENCH_STACK/HEAP_RESET()
- BENCH_PAUSE()     => lfs3_kiwibd_simpause() + BENCH_STACK/HEAP_PAUSE()
- BENCH_RESUME()    => lfs3_kiwibd_simresume() + BENCH_STACK/HEAP_RESUME()

This does two things:

1. Adds pause/resume counters to bd counters to make it easier to
   exclude operations from the current bench (potentially useful for
   seq+disk usage).

2. Exposes bd simtime operations as BENCH_* macros, to make it a bit
   easier to interact with simtime without tying all the benches to
   kiwibd. (Not that we'll ever probably not use kiwibd, but still).

Also adopted 32-bit counters for stack/heap pause state instead of a
32-bit stack. Not that either are at a risk of overflowing, but better
safe than sorry.
2026-03-09 22:54:49 -05:00

314 lines
9.2 KiB
TOML

# High-level write-throughput benchmarks
after = ['bench_file', 'bench_dir']
# these are common and can be overridden suite-wide
#
# note for bench_*_many, file size defaults to CHUNK, and SIZE = sum of
# all files
#
defines.SIZE = '1024*1024' # 1 MiB
defines.CHUNK = 64
defines.SEED = 42
# simulated time, in nanoseconds, to run the bench
defines.SIM_TIME = '60ULL*60ULL*1000ULL*1000ULL*1000ULL' # 1 hour
# simulation size in bytes
defines.SIM_SIZE = 0
# set this to true to skip bench warmup
defines.SKIP_WARMUP = false
# include common bench helpers
code = '''
#include "benches/bench_helpers.h"
'''
# sequential write throughput
[cases.bench_wt_seq]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (!SKIP_WARMUP) {
BENCH_PAUSE();
int err = bench_helpers_warmup(&lfs3);
if (err) {
LFS3_ERROR("Bench warmup failed: %d", err);
return;
}
BENCH_RESUME();
}
uint32_t prng = SEED;
// reset our timer
BENCH_SIMRESET();
// open a file
BENCH_START("write");
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "bench_linear",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_off_t size = 0;
uint64_t written = 0;
// ok, one of these needs to be non-zero
LFS3_ASSERT(SIM_TIME > 0 || SIM_SIZE > 0);
while (!(SIM_SIZE && written >= (uint64_t)SIM_SIZE)
&& !(SIM_TIME && BENCH_SIMTIME() >= (bench_ns_t)SIM_TIME)) {
// arguably we should just rewind and continue writing to the
// front of the file when we hit the end, but this overly
// penalizes littlefs2, so instead we truncate
if (size >= SIZE) {
lfs3_file_rewind(&lfs3, &file) => 0;
lfs3_file_truncate(&lfs3, &file, 0) => 0;
size = 0;
}
// write to the file
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK;
size += CHUNK;
written += CHUNK;
}
lfs3_file_close(&lfs3, &file) => 0;
// report the amount we managed to write
BENCH_STOP("write", written);
// report the total stack/heap usage after the benchmark
#ifdef BENCH_STACK
BENCH_RESULT("stack", written, BENCH_STACK_WATERMARK());
#endif
#ifdef BENCH_HEAP
BENCH_RESULT("heap", written, BENCH_HEAP_WATERMARK());
#endif
// find the total disk usage after the benchmark
//
// note this is garbage because of the above truncates!
uintmax_t usage = bench_helpers_usage(&lfs3);
BENCH_RESULT("usage", written, usage);
lfs3_unmount(&lfs3) => 0;
'''
# random write throughput
[cases.bench_wt_random]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (!SKIP_WARMUP) {
BENCH_PAUSE();
int err = bench_helpers_warmup(&lfs3);
if (err) {
return;
}
BENCH_RESUME();
}
uint32_t prng = SEED;
// reset our timer
BENCH_SIMRESET();
// open a file
BENCH_START("write");
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "bench_random",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_off_t size = 0;
uint64_t written = 0;
while (!(SIM_SIZE && written >= (uint64_t)SIM_SIZE)
&& !(SIM_TIME && BENCH_SIMTIME() >= (bench_ns_t)SIM_TIME)) {
// seek to a random location
lfs3_off_t pos = BENCH_PRNG(&prng) % SIZE;
lfs3_file_seek(&lfs3, &file, pos, LFS3_SEEK_SET) => pos;
// write to the file
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK;
size = lfs3_max(size, pos + CHUNK);
written += CHUNK;
}
lfs3_file_close(&lfs3, &file) => 0;
// report the amount we managed to write
BENCH_STOP("write", written);
// report the total stack/heap usage after the benchmark
#ifdef BENCH_STACK
BENCH_RESULT("stack", written, BENCH_STACK_WATERMARK());
#endif
#ifdef BENCH_HEAP
BENCH_RESULT("heap", written, BENCH_HEAP_WATERMARK());
#endif
// find the total disk usage after the benchmark
uintmax_t usage = bench_helpers_usage(&lfs3);
BENCH_RESULT("usage", written, usage);
lfs3_unmount(&lfs3) => 0;
'''
# logging write throughput
#
# two big differences from seq:
# 1. fruncate/rotations instead of rewind+truncate
# 2. sync called on every write
[cases.bench_wt_logging]
defines.NO_FRUNCATE = false
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (!SKIP_WARMUP) {
BENCH_PAUSE();
int err = bench_helpers_warmup(&lfs3);
if (err) {
return;
}
BENCH_RESUME();
}
uint32_t prng = SEED;
// reset our timer
BENCH_SIMRESET();
// open a file
BENCH_START("write");
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "bench_log",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0;
uint64_t written = 0;
// ok, one of these needs to be non-zero
LFS3_ASSERT(SIM_TIME > 0 || SIM_SIZE > 0);
while (!(SIM_SIZE && written >= (uint64_t)SIM_SIZE)
&& !(SIM_TIME && BENCH_SIMTIME() >= (bench_ns_t)SIM_TIME)) {
// append to log
uint8_t wbuf[CHUNK];
for (lfs3_size_t j = 0; j < CHUNK; j++) {
wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK;
// sync
lfs3_file_sync(&lfs3, &file) => 0;
// fruncate or rotate if full
lfs3_soff_t size = lfs3_file_size(&lfs3, &file);
assert(size >= 0);
if (size > SIZE) {
if (!NO_FRUNCATE) {
lfs3_file_fruncate(&lfs3, &file, SIZE) => 0;
} else {
lfs3_file_close(&lfs3, &file) => 0;
lfs3_rename(&lfs3, "bench_log", "bench_log.1") => 0;
lfs3_file_open(&lfs3, &file, "bench_log",
LFS3_O_WRONLY
| LFS3_O_CREAT
| LFS3_O_EXCL
| LFS3_O_APPEND) => 0;
}
}
written += CHUNK;
}
lfs3_file_close(&lfs3, &file) => 0;
// report the amount we managed to write
BENCH_STOP("write", written);
// report the total stack/heap usage after the benchmark
#ifdef BENCH_STACK
BENCH_RESULT("stack", written, BENCH_STACK_WATERMARK());
#endif
#ifdef BENCH_HEAP
BENCH_RESULT("heap", written, BENCH_HEAP_WATERMARK());
#endif
// find the total disk usage after the benchmark
uintmax_t usage = bench_helpers_usage(&lfs3);
BENCH_RESULT("usage", written, usage);
lfs3_unmount(&lfs3) => 0;
'''
# many small file write throughput
[cases.bench_wt_many]
defines.FILE_SIZE = 'CHUNK'
defines.FILE_COUNT = '(SIZE+(FILE_SIZE-1)) / lfs3_max(FILE_SIZE, 1)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
if (!SKIP_WARMUP) {
BENCH_PAUSE();
int err = bench_helpers_warmup(&lfs3);
if (err) {
return;
}
BENCH_RESUME();
}
uint32_t prng = SEED;
// reset our timer
BENCH_SIMRESET();
// open a file
BENCH_START("write");
uint64_t written = 0;
// ok, one of these needs to be non-zero
LFS3_ASSERT(SIM_TIME > 0 || SIM_SIZE > 0);
while (!(SIM_SIZE && written >= (uint64_t)SIM_SIZE)
&& !(SIM_TIME && BENCH_SIMTIME() >= (bench_ns_t)SIM_TIME)) {
// choose a random filename
lfs3_off_t pos = BENCH_PRNG(&prng) % FILE_COUNT;
char name[256];
sprintf(name, "bench_%08x", pos);
uint8_t wbuf[CHUNK];
// create the file
//
// note file == CHUNK here, the sum of all files should add up
// roughly to the benchmark SIZE
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, name,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0;
for (lfs3_size_t i = 0; i < (FILE_SIZE+(CHUNK-1))/CHUNK; i++) {
lfs3_ssize_t d = lfs3_min(CHUNK, FILE_SIZE);
memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), d);
lfs3_file_write(&lfs3, &file, wbuf, d) => d;
written += d;
// taking too long?
if (SIM_TIME && BENCH_SIMTIME() >= (bench_ns_t)SIM_TIME) {
break;
}
}
lfs3_file_close(&lfs3, &file) => 0;
}
// report the amount we managed to write
BENCH_STOP("write", written);
// report the total stack/heap usage after the benchmark
#ifdef BENCH_STACK
BENCH_RESULT("stack", written, BENCH_STACK_WATERMARK());
#endif
#ifdef BENCH_HEAP
BENCH_RESULT("heap", written, BENCH_HEAP_WATERMARK());
#endif
// find the total disk usage after the benchmark
uintmax_t usage = bench_helpers_usage(&lfs3);
BENCH_RESULT("usage", written, usage);
lfs3_unmount(&lfs3) => 0;
'''