rbyd: Prioritized y-split prunes, fixed remaining balance issues!

The issue with relying solely on the jump > branch hack to disambiguate
recolorable prunes is that suddenly we're back in ambiguous territory
when we encounter post-split yellow nodes. We need to prioritize yellow
prunes or else post-split weights can become ambiguous.

This was the whole reason we added a 3rd color!

Fortunately we can still prioritize yellow nodes by trying both
recolorable prunes first, and only falling back to non-recolorable
prunes if we still have unreachable alts.

Ends up with a bit of code duplication, but gets things working again.

---

Turns out this was all we needed to get our rbyd operations perfectly
balanced! Now all test_rbyd tests are passing:

  test_rbyd+balance before: 1671/385878 failed
  test_rbyd+balance after:     0/385878 failed (-100.0%)

And after running the full test suite, can confirm _all_ tests are
passing with LFS_ASSERTRBYDBALANCE. So I think we have some pretty
decent confidence our rbyd algorithm maintains balance, even with range
removals:

  test+balance before: 20614/631541 failed
  test+balance after:      0/631541 failed (-100.0%)

It was an open question if this was even possible, so it's nice to see
some evidence balanced range removals are not a problem.

---

The current implementation is a bit hacky, so we do take a hit to code
size. Though we may be able to claw this back after cleaning things up:

           code          stack          ctx
  before: 38580           2624          640
  after:  38784 (+0.5%)   2624 (+0.0%)  640 (+0.0%)
This commit is contained in:
Christopher Haster
2025-01-26 16:31:43 -06:00
parent d3fe99ed98
commit b6b16099fc
+69 -3
View File
@@ -3872,6 +3872,66 @@ trunk:;
}
}
// TODO hmmmm
if (lfsr_tag_isred(p[0].alt)
&& lfsr_tag_unreachable(
p[0].alt, p[0].weight,
lower_rid, upper_rid,
lower_tag, upper_tag)
&& p[0].jump > branch) {
// prune unreachable recolorable alts
// <r => <b
// .----'| .----'|
// | <b | |
// | .-'| | .--'
// 1 2 3 1 2 3 x
// this includes unreachable yellow alts in yellow splits
// <b >b
// .-'| .-'|
// <y | | |
// .-------'| | | |
// | <r | => | >b
// | .----' | .--------|-'|
// | | <b | <b |
// | | .----'| | .----'| |
// 1 2 3 4 4 1 2 3 4 4 1
LFS_DEBUG("%04x->%04x: yprune",
branch, lfsr_rbyd_eoff(rbyd));
alt &= ~LFSR_TAG_R;
lfsr_rbyd_p_pop(p);
}
if (lfsr_tag_unreachable2(
alt, weight,
p[0].alt, p[0].weight,
lower_rid, upper_rid,
lower_tag, upper_tag)
&& lfsr_tag_isred(p[0].alt)
&& jump > branch) {
// prune unreachable recolorable alts
// <r => <b
// .----'| .-------'|
// | <b | |
// | .-'| | .-----'
// 1 2 3 1 2 3 x
// this includes unreachable red alts in yellow splits
// <b >b
// .-'| .-'|
// <y | | <b
// .-------'| | .-----------|-'|
// | <r | => | | |
// | .----' | | | |
// | | <b | <b |
// | | .----'| | .----'| |
// 1 2 3 4 4 1 2 3 4 4 2
LFS_DEBUG("%04x->%04x: rprune",
branch, lfsr_rbyd_eoff(rbyd));
alt = (p[0].alt & ~LFSR_TAG_R) | (alt & LFSR_TAG_R);
alt &= ~LFSR_TAG_R;
weight = p[0].weight;
jump = p[0].jump;
lfsr_rbyd_p_pop(p);
}
// prune red alts
if (lfsr_tag_isred(p[0].alt)
&& lfsr_tag_unreachable(
@@ -3908,6 +3968,12 @@ trunk:;
p[0].alt, p[0].weight,
lower_rid, upper_rid,
lower_tag, upper_tag)) {
if (!lfsr_tag_isred(p[0].alt)
&& lfsr_tag_isred(alt)) {
LFS_DEBUG("%04x->%04x: would've zpruned",
branch, lfsr_rbyd_eoff(rbyd));
}
// prune unreachable recolorable alts
// <r => <b
// .----'| .-------'|
@@ -3943,8 +4009,8 @@ trunk:;
// | <b | |
// | .-'| | .--'
// 3 4 5 3 4 5 x
} else if (!p[0].alt) { //|| lfsr_tag_isred(alt)) {
LFS_DEBUG("%04x->%04x: ztrim",
} else if (!p[0].alt) {
LFS_DEBUG("%04x->%04x: zprune",
branch, lfsr_rbyd_eoff(rbyd));
branch = branch_;
continue;
@@ -3957,7 +4023,7 @@ trunk:;
// .-'| .--'
// 3 4 3 4 x
} else if (!lfsr_tag_isred(alt)) {
LFS_DEBUG("%04x->%04x: btrim",
LFS_DEBUG("%04x->%04x: bprune",
branch, lfsr_rbyd_eoff(rbyd));
alt = LFSR_TAG_ALT(
LFSR_TAG_B,