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
// no parent? can't merge
&& rid != -1) {
for (uint8_t i = 0; i < 2; i++) {
lfs_ssize_t sibling_rid;
// 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 the right sibling
if (rid+1 < (lfs_ssize_t)parent.weight) {
// 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,
sibling_rid, LFSR_TAG_NAME,
rid+1, LFSR_TAG_NAME,
&sibling_rid, &sibling_tag, &sibling_weight,
&sibling_data);
if (err) {
// no sibling? can't merge
if (err == LFS_ERR_NOENT) {
continue;
}
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
@@ -4011,22 +3992,71 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
&sibling, -1, -1,
NULL);
if (sibling_estimate < 0) {
return estimate;
return sibling_estimate;
}
// fits? try to merge
if ((lfs_size_t)(estimate + sibling_estimate)
< lfs->cfg->block_size/2) {
if (i == 1) {
// if we're merging our left sibling, swap our rbyds
// so our sibling is on the right
bid -= sibling.weight;
rid -= rbyd.weight;
goto merge;
}
}
rbyd_ = sibling;
sibling = rbyd;
rbyd = rbyd_;
// try the left sibling
if (rid-(lfs_ssize_t)rbyd.weight >= 0) {
// 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;
}
}