Files
littlefs/scripts
Christopher Haster a85f08cfe3 Dropped lazy grafting, but kept lazy crystallization
This merges LFS3_o_GRAFT into LFS3_o_UNCRYST, simplifying the file write
path and avoiding the mess that is ungrafted leaves.

---

This goes for a different lazy crystallization/grafting strategy that
was overlooked before. Instead of requiring all leaves to be both
crystallized and grafted, we allow leaves to be uncrystallied, but they
_must_ be grafted (in-tree) at all times.

This gets us most of the rewrite preformance of lazy-crystallization,
without needing to worry about out-of-date file leaves.

Out-of-date file leaves were a headache for both code cost and concerns
around confusing filesystem states and related bugs.

Note LFS3_o_UNCRYST gets some extra behavior here:

- LFS3_o_UNCRYST indicates when crystallization is _necessary_, and no
  longer when crystallization is _possible_.

  We already keep track of when crystallization is _possible_ via bptr's
  erased-state, and this lets us control recrystallization in
  lfs3_file_flush_ without erased-state-clearing hacks (which probably
  wouldn't work with the future ddtree).

- We opportunistically clear the UNCRYST flag if it's not possible for
  future lfs3_file_crystallize_ calls to make progress:
  - When we crystallize a full block
  - When we hit the end of the file
  - When we hit a hole
  - When we hit an unaligned block

---

Note this does impact performance!

Unlike true lazy grafting, eagerly grafting means we're always
committing to the bshrub/btree more than is strictly necessary, and this
translates to more frequent btree node erases/compactions.

Current simulated benchmarks show a ~3x increase (~20us -> ~60us) in
write times for linear file writes on NOR flash.

However:

- The moment you need unaligned progs, this performance optimization
  goes out the window, as we need to graft bptrs before any padding
  fragments.

- This only kicks in once we start crystallizing. So any writes <
  crystal_thresh (both in new files and in between blocks) are forced
  to commit to the bshrub/btree every flush.

  This risks a difficult to predict performance characteristic.

- If you sync frequently (logging), we're forced to crystallize/graft
  anyways.

- The performance hit can be alleviated with either larger writes or
  larger caches, though I realize this goes against littlefs's
  "RAM-not-required" mantra.

Worst case, we can always bring back "lazy grafting" as a
high-performance option in the future.

Though note the above concerns around in-between/pre crystallization
performance. This may only make sense when cache_size >= both prog_size
and crystal_thresh.

And of course, there's a significant code tradeoff!

           code          stack          ctx
  before: 38020           2456          656
  after:  37588 (-1.1%)   2472 (+0.7%)  656 (+0.0%)

Uh, ignore that stack cost. The simplified logic leads to more functions
being inlined, which makes a mess of our stack measurements because we
don't take shrinkwrapping into account.
2025-07-03 18:04:18 -05:00
..
2025-05-15 18:48:46 -05:00
2025-04-16 15:23:06 -05:00