From c33182b49be896c39c9ac17c64fb72fe99b12a33 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 21 Aug 2025 11:32:21 -0500 Subject: [PATCH] Relax recrystallization when fruncating/logging This was a nasty performance hole found while benchmarking. Basically, any time crystallization is triggered, the crystallization algorithm tries to pack as much data into as few blocks as possible. When fruncating (the common, and performance sensitive, use case being logging), this can lead to the algorithm rewriting fruncated blocks. What the crystallization algorithm doesn't realize, however, is that when fruncating/logging, we're probably going to fruncate again on the next call, so rewriting the block is a waste of effort. Worst case -- a 1 block file -- this can cause littlefs to rewrite the entire file on every append. --- The solution implemented here, which is a bit of a hack, is to use the actual block start for block alignment instead of the logical start-of-block referenced by our btree/bshrub. This solves the fruncating/logging performance hole, with the tradeoff of using more storage than is strictly necessary. This tradeoff is probably expected with logging however. Code changes minimal: code stack ctx before: 37024 2352 684 after: 37036 (+0.0%) 2352 (+0.0%) 684 (+0.0%) --- lfs3.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lfs3.c b/lfs3.c index 54a2e9d6..26a92e92 100644 --- a/lfs3.c +++ b/lfs3.c @@ -14062,17 +14062,22 @@ static int lfs3_file_flush_(lfs3_t *lfs3, lfs3_file_t *file, } // is our left neighbor in the same block? - if (crystal_start - (bid-(weight-1)) + // + // note we use the actual block start here! not the sliced + // view! this avoids excessive recrystallizations when + // fruncating + if (crystal_start - (bid-(weight-1)-lfs3_bptr_off(&bptr)) < lfs3->cfg->block_size && lfs3_bptr_size(&bptr) > 0) { crystal_start = bid-(weight-1); // no? is our left neighbor at least our left block neighbor? // align to block alignment - } else if (crystal_start - (bid-(weight-1)) + } else if (crystal_start - (bid-(weight-1)-lfs3_bptr_off(&bptr)) < 2*lfs3->cfg->block_size && lfs3_bptr_size(&bptr) > 0) { - crystal_start = bid-(weight-1) + lfs3->cfg->block_size; + crystal_start = bid-(weight-1)-lfs3_bptr_off(&bptr) + + lfs3->cfg->block_size; } }