Unrolled the sibling estimate loop in btree merge

This simplifies control flow at a code cost. Unrolling for now as it
avoids all of the iteration derived special handling (which obscures the
underlying logic) and it may be possible to recoup the code cost through
more shared branch utility functions.

            code          stack
  before:  20650           1712
  after:   20742 (+0.4%)   1712 (+0.0%)

May revert in the future.
This commit is contained in:
Christopher Haster
2023-08-22 00:28:42 -05:00
parent 20c036038a
commit f70e7fe709
+63 -33
View File
@@ -3952,40 +3952,21 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
if ((lfs_size_t)estimate <= lfs->cfg->block_size/4 if ((lfs_size_t)estimate <= lfs->cfg->block_size/4
// no parent? can't merge // no parent? can't merge
&& rid != -1) { && rid != -1) {
for (uint8_t i = 0; i < 2; i++) { // try the right sibling
lfs_ssize_t sibling_rid; if (rid+1 < (lfs_ssize_t)parent.weight) {
// try the right sibling
if (i == 0) {
sibling_rid = rid+1;
// no right sibling? can't merge
if (sibling_rid >= (lfs_ssize_t)parent.weight) {
continue;
}
// try the left sibling
} else {
sibling_rid = rid-rbyd.weight;
// no left sibling? can't merge
if (sibling_rid < 0) {
continue;
}
}
// try looking up the sibling // try looking up the sibling
// TODO do we really need to fetch sibling_weight if we get // TODO do we really need to fetch sibling_weight if we get
// it in our btree struct? // it in our btree struct?
lfs_ssize_t sibling_rid;
lfsr_tag_t sibling_tag; lfsr_tag_t sibling_tag;
lfs_size_t sibling_weight; lfs_size_t sibling_weight;
lfsr_data_t sibling_data; lfsr_data_t sibling_data;
err = lfsr_rbyd_lookupnext(lfs, &parent, err = lfsr_rbyd_lookupnext(lfs, &parent,
sibling_rid, LFSR_TAG_NAME, rid+1, LFSR_TAG_NAME,
&sibling_rid, &sibling_tag, &sibling_weight, &sibling_rid, &sibling_tag, &sibling_weight,
&sibling_data); &sibling_data);
if (err) { if (err) {
// no sibling? can't merge LFS_ASSERT(err != LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
continue;
}
return err; return err;
} }
@@ -4011,22 +3992,71 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
&sibling, -1, -1, &sibling, -1, -1,
NULL); NULL);
if (sibling_estimate < 0) { if (sibling_estimate < 0) {
return estimate; return sibling_estimate;
} }
// fits? try to merge // fits? try to merge
if ((lfs_size_t)(estimate + sibling_estimate) if ((lfs_size_t)(estimate + sibling_estimate)
< lfs->cfg->block_size/2) { < lfs->cfg->block_size/2) {
if (i == 1) { goto merge;
// if we're merging our left sibling, swap our rbyds }
// so our sibling is on the right }
bid -= sibling.weight;
rid -= rbyd.weight;
rbyd_ = sibling; // try the left sibling
sibling = rbyd; if (rid-(lfs_ssize_t)rbyd.weight >= 0) {
rbyd = rbyd_; // try looking up the sibling
// TODO do we really need to fetch sibling_weight if we get
// it in our btree struct?
lfs_ssize_t sibling_rid;
lfsr_tag_t sibling_tag;
lfs_size_t sibling_weight;
lfsr_data_t sibling_data;
err = lfsr_rbyd_lookupnext(lfs, &parent,
rid-rbyd.weight, LFSR_TAG_NAME,
&sibling_rid, &sibling_tag, &sibling_weight,
&sibling_data);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
if (sibling_tag == LFSR_TAG_NAME) {
err = lfsr_rbyd_lookup(lfs, &parent,
sibling_rid, LFSR_TAG_WIDE(STRUCT),
&sibling_tag, &sibling_data);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
} }
}
LFS_ASSERT(sibling_tag == LFSR_TAG_BRANCH);
err = lfsr_data_readbranch(lfs, &sibling_data, sibling_weight,
&sibling);
if (err) {
return err;
}
// estimate if our sibling will fit
lfs_ssize_t sibling_estimate = lfsr_rbyd_estimate(lfs,
&sibling, -1, -1,
NULL);
if (sibling_estimate < 0) {
return sibling_estimate;
}
// fits? try to merge
if ((lfs_size_t)(estimate + sibling_estimate)
< lfs->cfg->block_size/2) {
// if we're merging our left sibling, swap our rbyds
// so our sibling is on the right
bid -= sibling.weight;
rid -= rbyd.weight;
rbyd_ = sibling;
sibling = rbyd;
rbyd = rbyd_;
goto merge; goto merge;
} }
} }