Files
littlefs/runners/bench_defines.h
T
Christopher Haster 0ea11c1a0e runners: Bumped default crystal_thresh BLOCK_SIZE/8 -> BLOCK_SIZE/16
This has been adopted in external benchmarks for a while, as it manages
to push sequential write performance into a much better region of the
diminishing-returns curve.

But hey! Don't take my word for it, let's see the results from our new
bench_runner for the first time:

  NOR throughput             cs=1/8  cs=1/16
  bench_wt_seq+write        15180.0  29402.6 (+93.7%)
  bench_wt_random+write       876.2    957.3 (+9.3%)
  bench_wt_logging+write     2001.0   2153.4 (+7.6%)
  bench_wt_many+write         453.6    453.6 (+0.0%)

  NAND throughput            cs=1/8  cs=1/16
  bench_wt_seq+write        21778.7  22330.1 (+2.5%)
  bench_wt_random+write      3583.0   3637.2 (+1.5%)
  bench_wt_logging+write    10855.1  10977.1 (+1.1%)
  bench_wt_many+write          68.2     68.2 (+0.0%)

Though this doesn't really capture the tradeoffs related to file tails,
storage usage, etc.

In theory sequential writes are happy to start crystallizing as soon as
any data is written, but this leads to significant waste anytime you're
not going to write most of a block.
2026-03-09 22:51:42 -05:00

227 lines
11 KiB
C

// littlefs bench runner defines
#ifdef BENCH_INCLUDE
// DISK_GEOMETRY controls which simulation we use
// 0 => NOR flash (the default)
// 1 => NAND flash
#define DISK_MAP(define) \
((DISK_GEOMETRY == 0) ? NOR_##define \
: NAND_##define)
#endif
// preconfigured defines that control how benches run
#ifdef BENCH_DEFINE
// name value (overridable)
BENCH_DEFINE(DISK_SIZE, 128*1024*1024 )
BENCH_DEFINE(DISK_GEOMETRY, 0 )
BENCH_DEFINE(READ_SIZE, DISK_MAP(READ_SIZE) )
BENCH_DEFINE(PROG_SIZE, DISK_MAP(PROG_SIZE) )
BENCH_DEFINE(ERASE_SIZE, DISK_MAP(ERASE_SIZE) )
BENCH_DEFINE(BLOCK_SIZE, LFS3_MAX(ERASE_SIZE, 512) )
BENCH_DEFINE(BLOCK_COUNT, DISK_SIZE/LFS3_MAX(BLOCK_SIZE, 1) )
BENCH_DEFINE(BLOCK_RECYCLES, 100 )
BENCH_DEFINE(RCACHE_SIZE, LFS3_MAX(16, READ_SIZE) )
BENCH_DEFINE(PCACHE_SIZE, LFS3_MAX(16, PROG_SIZE) )
BENCH_DEFINE(FCACHE_SIZE, 16 )
BENCH_DEFINE(LOOKAHEAD_SIZE, 16 )
BENCH_DEFINE(GC_FLAGS, LFS3_GC_GC )
BENCH_DEFINE(GC_STEPS, 0 )
BENCH_DEFINE(GC_LOOKAHEAD_THRESH, -1 )
BENCH_DEFINE(GC_LOOKGBMAP_THRESH, -1 )
BENCH_DEFINE(GC_PREERASE_COUNT, -1 )
BENCH_DEFINE(GC_COMPACT_THRESH, 0 )
BENCH_DEFINE(SHRUB_SIZE, BLOCK_SIZE/4 )
BENCH_DEFINE(FRAGMENT_SIZE, LFS3_MIN(BLOCK_SIZE/16, 512) )
BENCH_DEFINE(CRYSTAL_THRESH, BLOCK_SIZE/16 )
BENCH_DEFINE(LOOKGBMAP_THRESH, BLOCK_COUNT/4 )
// don't bother simulating erases, this may be less realistic, but
// it's certainly faster!
BENCH_DEFINE(ERASE_VALUE, -1 )
BENCH_DEFINE(READ_WIDTH, DISK_MAP(READ_WIDTH) )
BENCH_DEFINE(PROG_WIDTH, DISK_MAP(PROG_WIDTH) )
BENCH_DEFINE(ERASE_WIDTH, DISK_MAP(ERASE_WIDTH) )
BENCH_DEFINE(READ_TIMING, DISK_MAP(READ_TIMING) )
BENCH_DEFINE(PROG_TIMING, DISK_MAP(PROG_TIMING) )
BENCH_DEFINE(ERASE_TIMING, DISK_MAP(ERASE_TIMING) )
BENCH_DEFINE(READED_TIMING, DISK_MAP(READED_TIMING) )
BENCH_DEFINE(PROGGED_TIMING, DISK_MAP(PROGGED_TIMING) )
BENCH_DEFINE(ERASED_TIMING, DISK_MAP(ERASED_TIMING) )
// NOR flash (DISK_GEOMETRY=0)
//
// based on w25q64jv:
// https://www.winbond.com/resource-files/
// W25Q64JV%20RevM%2012242024%20Plus.pdf
//
// note one thing unique to NOR flash is the extreme erase cost
//
// FR=104 MHz, quad prog (9.6 ns * 8/4)
// => +~19 ns for bus (not read!)
//
// simple per-byte sim:
// readed=40ns/B fR=50 MHz, quad read (20 ns * 8/4)
// progged=1582ns/B tPP=0.4 ms, page=256 (0.4 ms / 256 + bus)
// erased=10986ns/B tSE=45 ms, sector=4096 (45 ms / 4096)
//
// less-simple bus+buffer sim:
// read=0ns/B (no transaction cost)
// prog=1563ns/B tPP=0.4 ms, page=256 (0.4 ms / 256)
// erase=10986ns/B tSE=45 ms, sector=4096 (45 ms / 4096)
// readed=40ns/B fR=50 MHz, quad read (20 ns * 8/4)
// progged=19ns/B (bus)
// erased=0ns/B (no bus cost)
//
BENCH_DEFINE(NOR_READ_SIZE, 1 )
BENCH_DEFINE(NOR_PROG_SIZE, 1 )
BENCH_DEFINE(NOR_ERASE_SIZE, 4096 )
#ifdef BENCH_PERBYTE
BENCH_DEFINE(NOR_READ_WIDTH, 0 )
BENCH_DEFINE(NOR_PROG_WIDTH, 0 )
BENCH_DEFINE(NOR_ERASE_WIDTH, 0 )
BENCH_DEFINE(NOR_READ_TIMING, 0 )
BENCH_DEFINE(NOR_PROG_TIMING, 0 )
BENCH_DEFINE(NOR_ERASE_TIMING, 0 )
BENCH_DEFINE(NOR_READED_TIMING, 40 )
BENCH_DEFINE(NOR_PROGGED_TIMING, 1582 )
BENCH_DEFINE(NOR_ERASED_TIMING, 10986 )
#else
BENCH_DEFINE(NOR_READ_WIDTH, 0 )
BENCH_DEFINE(NOR_PROG_WIDTH, LFS3_MIN(256, BLOCK_SIZE) )
BENCH_DEFINE(NOR_ERASE_WIDTH, BLOCK_SIZE )
BENCH_DEFINE(NOR_READ_TIMING, 0 )
BENCH_DEFINE(NOR_PROG_TIMING, 1563 )
BENCH_DEFINE(NOR_ERASE_TIMING, 10986 )
BENCH_DEFINE(NOR_READED_TIMING, 40 )
BENCH_DEFINE(NOR_PROGGED_TIMING, 19 )
BENCH_DEFINE(NOR_ERASED_TIMING, 0 )
#endif
// NAND flash (DISK_GEOMETRY=1)
//
// based on w25n01gv:
// https://www.winbond.com/resource-files/W25N01GV%20Rev%20R%20070323.pdf
//
// FR=104 MHz, quad read/prog (9.6 ns * 8/4)
// => +~19 ns for bus
//
// simple per-byte sim:
// readed=31ns/B tRD1=25 us, p=2048, s=512 (25 us / 2048 + bus)
// progged=141ns/B tPP=250 us, p=2048, s=512 (250 us / 2048 + bus)
// erased=15ns/B tBE=2 ms, block=131072 (2 ms / 131072)
//
// less-simple bus+buffer sim:
// read=12ns/B tRD1=25 us, p=2048, s=512 (25 us / 2048)
// prog=122ns/B tPP=250 us, p=2048, s=512 (250 us / 2048)
// erase=15ns/B tBE=2 ms, block=131072 (2 ms / 131072)
// readed=19ns/B (bus)
// progged=19ns/B (bus)
// erased=0ns/B (no bus cost)
//
BENCH_DEFINE(NAND_READ_SIZE, 1 )
BENCH_DEFINE(NAND_PROG_SIZE, 512 )
BENCH_DEFINE(NAND_ERASE_SIZE, 131072 )
#ifdef BENCH_PERBYTE
BENCH_DEFINE(NAND_READ_WIDTH, 0 )
BENCH_DEFINE(NAND_PROG_WIDTH, 0 )
BENCH_DEFINE(NAND_ERASE_WIDTH, 0 )
BENCH_DEFINE(NAND_READ_TIMING, 0 )
BENCH_DEFINE(NAND_PROG_TIMING, 0 )
BENCH_DEFINE(NAND_ERASE_TIMING, 0 )
BENCH_DEFINE(NAND_READED_TIMING, 31 )
BENCH_DEFINE(NAND_PROGGED_TIMING, 141 )
BENCH_DEFINE(NAND_ERASED_TIMING, 15 )
#else
BENCH_DEFINE(NAND_READ_WIDTH, LFS3_MIN(2048, BLOCK_SIZE) )
BENCH_DEFINE(NAND_PROG_WIDTH, LFS3_MIN(2048, BLOCK_SIZE) )
BENCH_DEFINE(NAND_ERASE_WIDTH, BLOCK_SIZE )
BENCH_DEFINE(NAND_READ_TIMING, 12 )
BENCH_DEFINE(NAND_PROG_TIMING, 122 )
BENCH_DEFINE(NAND_ERASE_TIMING, 15 )
BENCH_DEFINE(NAND_READED_TIMING, 19 )
BENCH_DEFINE(NAND_PROGGED_TIMING, 19 )
BENCH_DEFINE(NAND_ERASED_TIMING, 0 )
#endif
#endif
// struct lfs3_cfg definition
#ifdef BENCH_CFG
struct lfs3_cfg _cfg = {
#ifdef BENCH_CFG_CFG
BENCH_CFG_CFG
#endif
.read_size = READ_SIZE,
.prog_size = PROG_SIZE,
.block_size = BLOCK_SIZE,
.block_count = BLOCK_COUNT,
.block_recycles = BLOCK_RECYCLES,
.rcache_size = RCACHE_SIZE,
.pcache_size = PCACHE_SIZE,
.fcache_size = FCACHE_SIZE,
.lookahead_size = LOOKAHEAD_SIZE,
#ifdef LFS3_GBMAP
.gc_lookgbmap_thresh = GC_LOOKGBMAP_THRESH,
.lookgbmap_thresh = LOOKGBMAP_THRESH,
#endif
#ifdef LFS3_PREERASE
.gc_preerase_count = GC_PREERASE_COUNT,
#endif
#ifdef LFS3_GC
.gc_flags = GC_FLAGS,
.gc_steps = GC_STEPS,
#endif
.gc_lookahead_thresh = GC_LOOKAHEAD_THRESH,
.gc_compact_thresh = GC_COMPACT_THRESH,
.shrub_size = SHRUB_SIZE,
.fragment_size = FRAGMENT_SIZE,
.crystal_thresh = CRYSTAL_THRESH,
};
struct lfs3_cfg *BENCH_CFG = &_cfg;
#endif
// struct lfs3_*bd_cfg definition
#ifdef BENCH_BDCFG
#ifndef BENCH_KIWIBD
struct lfs3_emubd_cfg _bdcfg = {
#ifdef BENCH_BDCFG_CFG
BENCH_BDCFG_CFG
#endif
.erase_value = ERASE_VALUE,
.read_width = READ_WIDTH,
.prog_width = PROG_WIDTH,
.erase_width = ERASE_WIDTH,
.read_timing = READ_TIMING,
.prog_timing = PROG_TIMING,
.erase_timing = ERASE_TIMING,
.readed_timing = READED_TIMING,
.progged_timing = PROGGED_TIMING,
.erased_timing = ERASED_TIMING,
.erase_cycles = ERASE_CYCLES,
.badblock_behavior = BADBLOCK_BEHAVIOR,
.powerloss_behavior = POWERLOSS_BEHAVIOR,
.seed = BD_SEED,
};
struct lfs3_emubd_cfg *BENCH_BDCFG = &_bdcfg;
#else
struct lfs3_kiwibd_cfg _bdcfg = {
#ifdef BENCH_BDCFG_CFG
BENCH_BDCFG_CFG
#endif
.erase_value = ERASE_VALUE,
.read_width = READ_WIDTH,
.prog_width = PROG_WIDTH,
.erase_width = ERASE_WIDTH,
.read_timing = READ_TIMING,
.prog_timing = PROG_TIMING,
.erase_timing = ERASE_TIMING,
.readed_timing = READED_TIMING,
.progged_timing = PROGGED_TIMING,
.erased_timing = ERASED_TIMING,
};
struct lfs3_kiwibd_cfg *BENCH_BDCFG = &_bdcfg;
#endif
#endif