gc: Simplified gc heuristics, prioritize lookahead work

This is a number of tweaks intended to (1) minimize unexpected gc
latency due to last-minute lookahead scans, while (2) keeping the gc
logic simple and easy to reason about:

- Switched to using lfs3->flags directly for the is-work-done predicate.

  This ensures lfs3_fs_gc_ never terminates until the requested work is
  done, at the risk of, well, never terminating.

  But the previous "pending" variables had the same risk (set
  gc_compact_thresh=0 for example), it just removed the risk of
  non-termination due to conflicting gc requests. In both cases
  gc_compact_thresh has the biggest risk of non-termination.

- Prioritize lookahead scans before anything that can allocate
  (MKCONSISTENT, COMPACT, etc).

- Dropped aborting useless traversals (ckpointed lookaheads mainly).

  It's a good idea, but surprisingly complicated to decide when we
  should abort for all traversals. COMPACT, CKMETA, for example, are
  still useful to continue even if they can't prove anything about the
  system.

  Though now that I'm writing this, I'm wondering what the argument
  against LOOKAHEAD aborting is. Maybe this should be reverted for
  LOOKAHEAD as a special case...

Code changes minimal:

                 code          stack          ctx
  before:       35152           2136          660
  after:        35124 (-0.1%)   2136 (+0.0%)  660 (+0.0%)

                 code          stack          ctx
  gbmap before: 38076           2136          776
  gbmap after:  38048 (-0.1%)   2136 (+0.0%)  776 (+0.0%)
This commit is contained in:
Christopher Haster
2025-12-05 01:47:44 -06:00
parent d54fef8099
commit c34da290a5
+17 -25
View File
@@ -16355,34 +16355,28 @@ static int lfs3_fs_gc_(lfs3_t *lfs3, lfs3_mgc_t *mgc,
#endif
// do we have any pending work?
uint32_t pending = flags & lfs3->flags & LFS3_GC_ALL;
while (pending && (lfs3_off_t)steps > 0) {
while ((flags & (lfs3->flags & LFS3_GC_ALL))
&& (lfs3_off_t)steps > 0) {
// start a new traversal?
if (!lfs3_handle_isopen(lfs3, &mgc->t.h)) {
lfs3_mgc_init(mgc, pending);
lfs3_mgc_init(mgc, flags & (lfs3->flags & LFS3_GC_ALL));
lfs3_handle_open(lfs3, &mgc->t.h);
}
// don't bother with lookahead/gbmap if we've ckpointed
#ifndef LFS3_RDONLY
if (lfs3_t_isckpointed(mgc->t.h.flags)) {
mgc->t.h.flags &= ~LFS3_T_LOOKAHEAD;
}
#endif
// prioritize lookahead/gbmap before any work that may need to
// allocate
if (lfs3_t_islookahead(mgc->t.h.flags)) {
mgc->t.h.flags &= ~(
LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT)
| LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACT));
}
// will this traversal still make progress? no? start over
if (!(mgc->t.h.flags & LFS3_GC_ALL)) {
lfs3_handle_close(lfs3, &mgc->t.h);
continue;
}
// do we really need a full traversal?
if (!(mgc->t.h.flags & (
LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD)
| LFS3_GC_CKMETA
| LFS3_GC_CKDATA))) {
mgc->t.h.flags |= LFS3_T_MTREEONLY;
// do we really need a full traversal?
if (!(mgc->t.h.flags & (
LFS3_IFDEF_RDONLY(0, LFS3_GC_LOOKAHEAD)
| LFS3_GC_CKMETA
| LFS3_GC_CKDATA))) {
mgc->t.h.flags |= LFS3_T_MTREEONLY;
}
}
// progress gc
@@ -16397,8 +16391,6 @@ static int lfs3_fs_gc_(lfs3_t *lfs3, lfs3_mgc_t *mgc,
// end of traversal?
if (tag == LFS3_ERR_NOENT) {
lfs3_handle_close(lfs3, &mgc->t.h);
// clear any pending flags we make progress on
pending &= lfs3->flags & LFS3_GC_ALL;
}
// decrement steps