Reworked ftree/bshrub/shrub relationship, staging in ftree now

This is an attempt to simplify things a bit by moving more logic into
the ftree layer, instead of spreading things around between the
bshrub/bsprout functions.

Now, functionality is organized into high-level ftree operations and
low-level shrub/sprout operations, which only care about the inlined
portion of the shrub/sprout. No more lfsr_bshrub_commit/
lfsr_bshrub_commit__ which were mostly unrelated.

This also adds a lfsr_shrub_t type, which, by taking advantage of the
unused write-related rbyd fields to store the shrub estimate, has the
same size as lfsr_rbyd_t, but can still be casted to an rbyd/btree for
use in readonly rbyd/btree functions.

I considered merging shrub/sprout esimate and shrub/sprout compact into
some sort of ftree_estimate/compact, but it's not obvious what the
benefit would be, so leaving that on the table for now.

---

One nice change is our staging copies are now at the ftree level
(ftree.u and ftree.u_, maybe not the best names, but this is what I've
been using for unions where the name doesn't really matter, god I want
unnamed unions). This simplifies staging, and avoids staging issues
where the underlying type changes.

---

A bit unrelated, but necessary to integrate lfsr_ftree_traverse, a
generalized lfsr_tinfo_t type for all traversal functions was added
(adopted from lfsr_traversal_t really). This is a straightforward tagged
union with relevant traversal types.

The benefit of a generalized tinfo type is better chance we can just
pass the tinfo pointer through multiple layers.

Code changes:

            code          stack
  before:  33368           2984
  after:   33260 (-0.3%)   3024 (+1.3%)
This commit is contained in:
Christopher Haster
2024-01-09 09:45:02 -06:00
parent f209c95dad
commit b0bd026b87
3 changed files with 801 additions and 830 deletions
+732 -764
View File
File diff suppressed because it is too large Load Diff
+31 -26
View File
@@ -375,6 +375,7 @@ typedef struct lfsr_rbyd {
uint32_t cksum; uint32_t cksum;
} lfsr_rbyd_t; } lfsr_rbyd_t;
// a btree is just the root rbyd
typedef lfsr_rbyd_t lfsr_btree_t; typedef lfsr_rbyd_t lfsr_btree_t;
typedef struct lfsr_mptr { typedef struct lfsr_mptr {
@@ -456,13 +457,6 @@ typedef struct lfsr_data {
} u; } u;
} lfsr_data_t; } lfsr_data_t;
typedef struct lfsr_bptr {
// note data.size lines up with weight in lfsr_btree_t
lfsr_data_t data;
lfs_size_t cksize;
uint32_t cksum;
} lfsr_bptr_t;
// erased-state checksum // erased-state checksum
typedef struct lfsr_ecksum { typedef struct lfsr_ecksum {
// size=-1 indicates no ecksum // size=-1 indicates no ecksum
@@ -510,12 +504,12 @@ typedef struct lfs_file {
const struct lfs_file_config *cfg; const struct lfs_file_config *cfg;
} lfs_file_t; } lfs_file_t;
// bsprouts must always be associated with an mdir typedef struct lfsr_bptr {
typedef struct lfsr_bsprout { // note data.size lines up with weight in lfsr_btree_t
lfsr_data_t data; lfsr_data_t data;
// copy for staging lfs_size_t cksize;
lfsr_data_t data_; uint32_t cksum;
} lfsr_bsprout_t; } lfsr_bptr_t;
// a bleaf is just a bptr with all optional attrs // a bleaf is just a bptr with all optional attrs
typedef struct lfsr_bleaf { typedef struct lfsr_bleaf {
@@ -523,29 +517,40 @@ typedef struct lfsr_bleaf {
lfsr_ecksum_t becksum; lfsr_ecksum_t becksum;
} lfsr_bleaf_t; } lfsr_bleaf_t;
// bshrubs must always be associated with an mdir // a shrub is a secondary trunk in an mdir, we really only need
// // trunk/weight/block, so we sneak our estimate into some
// rbyd.block == mdir.blocks[0] => bshrub // overlapping fields
// rbyd.block != mdir.blocks[0] => btree typedef struct lfsr_shrub {
typedef struct lfsr_bshrub { // this all lines up with lfsr_rbyd_t
lfsr_rbyd_t rbyd; lfsr_srid_t weight;
// copy for staging lfs_block_t blocks[2];
lfsr_rbyd_t rbyd_; lfs_size_t trunk;
lfs_size_t eoff;
// an upper-bound estimate on the on-disk shrub size // an upper-bound estimate on the on-disk shrub size
lfs_size_t estimate; lfs_size_t estimate;
} lfsr_bshrub_t; } lfsr_shrub_t;
// the lfsr_ftree_t struct is a sort of proto-file // the lfsr_ftree_t struct is a sort of proto-file
typedef struct lfsr_ftree { typedef struct lfsr_ftree {
// ftrees contain both an active tree and staging tree, to allow
// staging files during mdir compacts
//
// navigating this union is a bit tricky, and relies on related
// mdir's block:
//
// sign(size)=1, data.size==0 => bnull
// sign(size)=1, data.block==mdir.block => bsprout
// sign(size)=1, data.block!=mdir.block => bleaf
// sign(size)=0, data.block==mdir.block => bshrub
// sign(size)=0, data.block!=mdir.block => btree
//
union { union {
lfs_soff_t size; lfs_soff_t size;
lfsr_data_t data; lfsr_data_t bsprout;
lfsr_bsprout_t bsprout;
lfsr_bptr_t bptr;
lfsr_bleaf_t bleaf; lfsr_bleaf_t bleaf;
lfsr_bshrub_t bshrub; lfsr_shrub_t bshrub;
lfsr_btree_t btree; lfsr_btree_t btree;
} u; } u, u_;
} lfsr_ftree_t; } lfsr_ftree_t;
typedef struct lfsr_file { typedef struct lfsr_file {
+38 -40
View File
@@ -4294,37 +4294,36 @@ code = '''
// a bit hacky, but this catches infinite loops // a bit hacky, but this catches infinite loops
assert(i <= 2*N); assert(i <= 2*N);
lfsr_binfo_t binfo; lfsr_bid_t bid;
int err = lfsr_btree_traverse(&lfs, &btree, &traversal, &binfo); lfsr_tinfo_t tinfo;
int err = lfsr_btree_traverse(&lfs, &btree, &traversal,
&bid, &tinfo);
assert(!err || err == LFS_ERR_NOENT); assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) { if (err == LFS_ERR_NOENT) {
break; break;
} }
if (binfo.tag == LFSR_TAG_BRANCH) { if (tinfo.tag == LFSR_TAG_BRANCH) {
printf("traversal: %d 0x%x w%d btree 0x%x.%x\n", printf("traversal: %d 0x%x btree 0x%x.%x\n",
binfo.bid, bid,
binfo.tag, tinfo.tag,
binfo.weight, tinfo.u.rbyd.blocks[0], tinfo.u.rbyd.trunk);
binfo.u.rbyd.blocks[0], binfo.u.rbyd.trunk);
// keep track of seen blocks // keep track of seen blocks
seen[binfo.u.rbyd.blocks[0] / 8] seen[tinfo.u.rbyd.blocks[0] / 8]
|= 1 << (binfo.u.rbyd.blocks[0] % 8); |= 1 << (tinfo.u.rbyd.blocks[0] % 8);
} else if (binfo.tag == LFSR_TAG_DATA) { } else if (tinfo.tag == LFSR_TAG_DATA) {
printf("traversal: %d 0x%x w%d data %d\n", printf("traversal: %d 0x%x data %d\n",
binfo.bid, bid,
binfo.tag, tinfo.tag,
binfo.weight, lfsr_data_size(&tinfo.u.data));
lfsr_data_size(&binfo.u.data));
} else { } else {
// well this shouldn't happen // well this shouldn't happen
printf("traversal: %d 0x%x w%d\n", printf("traversal: %d 0x%x\n",
binfo.bid, bid,
binfo.tag, tinfo.tag);
binfo.weight);
assert(false); assert(false);
} }
} }
@@ -4447,37 +4446,36 @@ code = '''
// a bit hacky, but this catches infinite loops // a bit hacky, but this catches infinite loops
assert(i <= 2*N); assert(i <= 2*N);
lfsr_binfo_t binfo; lfsr_bid_t bid;
int err = lfsr_btree_traverse(&lfs, &btree, &traversal, &binfo); lfsr_tinfo_t tinfo;
int err = lfsr_btree_traverse(&lfs, &btree, &traversal,
&bid, &tinfo);
assert(!err || err == LFS_ERR_NOENT); assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) { if (err == LFS_ERR_NOENT) {
break; break;
} }
if (binfo.tag == LFSR_TAG_BRANCH) { if (tinfo.tag == LFSR_TAG_BRANCH) {
printf("traversal: %d 0x%x w%d btree 0x%x.%x\n", printf("traversal: %d 0x%x btree 0x%x.%x\n",
binfo.bid, bid,
binfo.tag, tinfo.tag,
binfo.weight, tinfo.u.rbyd.blocks[0], tinfo.u.rbyd.trunk);
binfo.u.rbyd.blocks[0], binfo.u.rbyd.trunk);
// keep track of seen blocks // keep track of seen blocks
seen[binfo.u.rbyd.blocks[0] / 8] seen[tinfo.u.rbyd.blocks[0] / 8]
|= 1 << (binfo.u.rbyd.blocks[0] % 8); |= 1 << (tinfo.u.rbyd.blocks[0] % 8);
} else if (binfo.tag == LFSR_TAG_DATA) { } else if (tinfo.tag == LFSR_TAG_DATA) {
printf("traversal: %d 0x%x w%d data %d\n", printf("traversal: %d 0x%x data %d\n",
binfo.bid, bid,
binfo.tag, tinfo.tag,
binfo.weight, lfsr_data_size(&tinfo.u.data));
lfsr_data_size(&binfo.u.data));
} else { } else {
// well this shouldn't happen // well this shouldn't happen
printf("traversal: %d 0x%x w%d\n", printf("traversal: %d 0x%x\n",
binfo.bid, bid,
binfo.tag, tinfo.tag);
binfo.weight);
assert(false); assert(false);
} }
} }