mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-12-01 12:20:02 +00:00
Prevent oscillation when crystal_thresh < fragment_size
When crystal_thresh < fragment_size, there was a risk that repeated
write operations would oscillate between crystallizing and fragmenting
every operation. Not only would this wreck performance, it would also
violently wear down blocks as each crystallization would trigger an
erase.
Fortunately all we need to do to prevent this is check both
fragment_size and crystal_thresh before fragmenting. Note this also
affects the fragment checks in truncate/fruncate.
---
crystal_thresh < fragment_size is kind of a weird configuration, to be
honest we should probably just assert if configured this way (we never
write fragments > crystal_thresh, because at that point we would just
crystallize).
But at the moment the extra leniency is useful for benchmarking.
Adds a bit of code, but will probably either assert or mutably limit in
the future:
code stack ctx
before: 37028 2352 684
after: 37076 (+0.1%) 2352 (+0.0%) 684 (+0.0%)
This commit is contained in:
18
lfs3.c
18
lfs3.c
@ -13268,7 +13268,9 @@ static int lfs3_file_graft_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// carve fragment?
|
||||
} else if (!lfs3_bptr_isbptr(&bptr_)
|
||||
// carve bptr into fragment?
|
||||
|| lfs3_bptr_size(&l) <= lfs3->cfg->fragment_size) {
|
||||
|| (lfs3_bptr_size(&l) <= lfs3->cfg->fragment_size
|
||||
&& lfs3_bptr_size(&l)
|
||||
< lfs3_max(lfs3->cfg->crystal_thresh, 1))) {
|
||||
rattrs[rattr_count++] = LFS3_RATTR_DATA(
|
||||
LFS3_TAG_GROW | LFS3_TAG_MASK8 | LFS3_TAG_DATA,
|
||||
-(bid+1 - pos),
|
||||
@ -13316,7 +13318,9 @@ static int lfs3_file_graft_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// carve fragment?
|
||||
} else if (!lfs3_bptr_isbptr(&bptr_)
|
||||
// carve bptr into fragment?
|
||||
|| lfs3_bptr_size(&r) <= lfs3->cfg->fragment_size) {
|
||||
|| (lfs3_bptr_size(&r) <= lfs3->cfg->fragment_size
|
||||
&& lfs3_bptr_size(&r)
|
||||
< lfs3_max(lfs3->cfg->crystal_thresh, 1))) {
|
||||
r_rattr_ = LFS3_RATTR_DATA(
|
||||
LFS3_TAG_DATA, bid+1 - (pos+weight),
|
||||
&r.d);
|
||||
@ -14964,8 +14968,9 @@ int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
||||
// discard if our leaf is a fragment, is fragmented, or is completed
|
||||
// truncated, we can't rely on any in-bshrub/btree state
|
||||
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
||||
|| lfs3_bptr_size(&file->leaf.bptr)
|
||||
<= lfs3->cfg->fragment_size) {
|
||||
|| (lfs3_bptr_size(&file->leaf.bptr) <= lfs3->cfg->fragment_size
|
||||
&& lfs3_bptr_size(&file->leaf.bptr)
|
||||
< lfs3_max(lfs3->cfg->crystal_thresh, 1))) {
|
||||
lfs3_file_discardleaf(file);
|
||||
}
|
||||
|
||||
@ -15056,8 +15061,9 @@ int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
||||
// discard if our leaf is a fragment, is fragmented, or is completed
|
||||
// truncated, we can't rely on any in-bshrub/btree state
|
||||
if (!lfs3_bptr_isbptr(&file->leaf.bptr)
|
||||
|| lfs3_bptr_size(&file->leaf.bptr)
|
||||
<= lfs3->cfg->fragment_size) {
|
||||
|| (lfs3_bptr_size(&file->leaf.bptr) <= lfs3->cfg->fragment_size
|
||||
&& lfs3_bptr_size(&file->leaf.bptr)
|
||||
< lfs3_max(lfs3->cfg->crystal_thresh, 1))) {
|
||||
lfs3_file_discardleaf(file);
|
||||
}
|
||||
|
||||
|
||||
3
lfs3.h
3
lfs3.h
@ -566,6 +566,9 @@ struct lfs3_cfg {
|
||||
// allow crystal_thresh=0? crystal_thresh=0 => block_size/16 or
|
||||
// block_size/8 is probably a better default. need to benchmark.
|
||||
|
||||
// TODO we should probably just assert if crystal_thresh < fragment_size,
|
||||
// or if crystal_thresh < prog_size, these aren't really valid cases
|
||||
|
||||
// Threshold for compacting multiple fragments into a block. Smaller
|
||||
// values will crystallize more eagerly, reducing disk usage, but
|
||||
// increasing the cost of random-writes.
|
||||
|
||||
Reference in New Issue
Block a user