10e77d9177
- 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.
314 lines
9.2 KiB
TOML
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;
|
|
'''
|
|
|