gbmap: Added gc_repoplookahead_thresh and gc_repopgbmap_thresh

To allow relaxing when LFS3_I_REPOPLOOKAHEAD and LFS3_I_REPOPGBMAP will
be set, potentially reducing gc workload after allocating only a couple
blocks.

The relevant cfg comments have quite a bit more info.

Note -1 (not the default, 0, maybe we should explicitly flip this?)
restores the previous functionality of setting these flags on the first
block allocation.

---

Also tweaked gbmap repops during gc/traversals to _not_ try to repop
unless LFS3_I_REPOPGBMAP is set. We probably should have done this from
the beginning since repopulating the gbmap writes to disk and is
potentially destructive.

Adds code, though hopefully we can claw this back with future config
rework:

                 code          stack          ctx
  before:       37176           2352          684
  after:        37208 (+0.1%)   2352 (+0.0%)  688 (+0.6%)

                 code          stack          ctx
  gbmap before: 40024           2368          848
  gbmap after:  40120 (+0.2%)   2368 (+0.0%)  856 (+0.9%)
This commit is contained in:
Christopher Haster
2025-10-17 00:32:03 -05:00
parent 1dc1a26f11
commit 12874bff76
5 changed files with 394 additions and 88 deletions
+224
View File
@@ -161,6 +161,117 @@ code = '''
lfs3_unmount(&lfs3) => 0;
'''
# test that we can relax lookahead repopulation with
# gc_repoplookahead_thresh
[cases.test_gc_repoplookahead_relaxed]
# relax our repop thresh
defines.GC_REPOPLOOKAHEAD_THRESH = [
'-1',
'8*LOOKAHEAD_SIZE - (8*LOOKAHEAD_SIZE/4)',
'8*LOOKAHEAD_SIZE - (8*LOOKAHEAD_SIZE/2)',
]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.GC_FLAGS = '''
LFS3_GC_REPOPLOOKAHEAD
| ((CKMETA) ? LFS3_GC_CKMETA : 0)
| ((CKDATA) ? LFS3_GC_CKDATA : 0)
'''
defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000]
defines.SIZE = 'BLOCK_SIZE'
ifdef = 'LFS3_GC'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3,
LFS3_F_RDWR
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_fs_gc(&lfs3) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (!(fsinfo.flags & LFS3_I_REPOPLOOKAHEAD)) {
break;
}
}
// check the file contents
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
// rewrite file until we need to repopulate
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (fsinfo.flags & LFS3_I_REPOPLOOKAHEAD) {
// check that we actually relaxed repopulations
if ((lfs3_size_t)GC_REPOPLOOKAHEAD_THRESH
< (lfs3_size_t)(8*LOOKAHEAD_SIZE-1)) {
assert(i > 0);
} else {
assert(i == 0);
}
break;
}
}
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_fs_gc(&lfs3) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (!(fsinfo.flags & LFS3_I_REPOPLOOKAHEAD)) {
break;
}
}
// check the file contents
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that repopgbmap can make progress in isolation
[cases.test_gc_repopgbmap_progress]
@@ -314,6 +425,119 @@ code = '''
lfs3_unmount(&lfs3) => 0;
'''
# test that we can relax gbmap repopulation with gc_repopgbmap_thresh
[cases.test_gc_repopgbmap_relaxed]
# relax our repop thresh
defines.GC_REPOPGBMAP_THRESH = [
'-1',
'BLOCK_COUNT - (BLOCK_COUNT/4)',
'BLOCK_COUNT - (BLOCK_COUNT/2)',
]
defines.REPOPLOOKAHEAD = [false, true]
defines.CKMETA = [false, true]
defines.CKDATA = [false, true]
defines.GC_FLAGS = '''
LFS3_GC_REPOPGBMAP
| ((REPOPLOOKAHEAD) ? LFS3_GC_REPOPLOOKAHEAD : 0)
| ((CKMETA) ? LFS3_GC_CKMETA : 0)
| ((CKDATA) ? LFS3_GC_CKDATA : 0)
'''
defines.GC_STEPS = [-1, 1, 2, 10, 100, 1000]
defines.SIZE = 'BLOCK_SIZE'
if = 'GBMAP'
ifdef = ['LFS3_GC', 'LFS3_GBMAP']
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3,
LFS3_F_RDWR
| ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0),
CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint32_t prng = 42;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_fs_gc(&lfs3) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (!(fsinfo.flags & LFS3_I_REPOPGBMAP)) {
break;
}
}
// check the file contents
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
uint8_t rbuf[SIZE];
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
// rewrite file until we need to repopulate
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_file_open(&lfs3, &file, "spider",
LFS3_O_WRONLY | LFS3_O_TRUNC) => 0;
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
lfs3_file_close(&lfs3, &file) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (fsinfo.flags & LFS3_I_REPOPGBMAP) {
// check that we actually relaxed repopulations
if ((lfs3_size_t)GC_REPOPGBMAP_THRESH
< BLOCK_COUNT-1) {
assert(i > 0);
} else {
assert(i == 0);
}
break;
}
}
// run GC until we make progress
for (lfs3_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
LFS3_ASSERT(i < 2*BLOCK_COUNT);
lfs3_fs_gc(&lfs3) => 0;
struct lfs3_fsinfo fsinfo;
lfs3_fs_stat(&lfs3, &fsinfo) => 0;
if (!(fsinfo.flags & LFS3_I_REPOPGBMAP)) {
break;
}
}
// check the file contents
lfs3_file_open(&lfs3, &file, "spider", LFS3_O_RDONLY) => 0;
lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that compactmeta can make progress in isolation
[cases.test_gc_compactmeta_progress]