From f70e7fe7093f47059aa4ec84a9860424fbf33dd5 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 22 Aug 2023 00:28:42 -0500 Subject: [PATCH] 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. --- lfs.c | 96 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/lfs.c b/lfs.c index 6ba203ba..fb8d1d08 100644 --- a/lfs.c +++ b/lfs.c @@ -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; } }