From 1ce47bfc47d1b8be0e3b608e3fa79bcd94cc9173 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 31 Mar 2024 12:55:28 -0500 Subject: [PATCH] rbyd-rr: Implemented coloring during rbyd compaction This tweaks our rbyd compaction algorithm to color the alts correctly to represent a balanced 2-3-4 tree. Previously, we didn't really care about coloring the compacted tree, because we didn't really care about color when pruning unreachable alts. But now that we refuse to prune isolated black alts, or risk unbalancing the underlying 2-3-4 tree, it's important we color the compacted tree correctly. Otherwise the unreachable alts that terminate our binary nodes will just never be pruned, unbalancing each layer of the tree by ~1. Compaction without coloring: tags: effective rby tree: data a <. .---> a data b <--. .---b-b-> b data c <----. | .---> c data d <------. b-b-b-b-> d altble a <. | | | altble b -|-' | | null | | | effective 2-3-4 tree: altble c <--.-' | .---o -. altble d -|-|---' | o | null | | .-o .-o +- h=4 altble b -' | | o | o | altble d ---' a b c d -' null Compaction with coloring: tags: effective rby tree: data a <. .---> a data b <--. .---r-b-> b data c <----. | .---> c data d <------. r-b-r-b-> d altrle a <. | | | altble b -|-' | | null | | | effective 2-3-4 tree: altrle c <--.-' | .---o -. altble d -|-|---' .-o .-o +- h=2 null | | a b c d -' altrle b -' | altble d ---' null Note that if the compacted tree is not full, i.e. not a power-of-two, we need to make sure the resulting unary nodes are still colored black. Isolated red alts are not allowed and would create even more hilarious problems. Fortunately there is just enough context in lfsr_rbyd_appendcompaction, since we know exactly where each layer ends, to determine if each node is binary or unary without needing to attempt to read unnecessary tags. It's also worth noting the resulting unary nodes may seem like an unnecessary side effect, but they are actually quite useful here for preserving the underlying 2-3-4 balance! In the same way unary nodes preserve the 2-3-4 balance during range operations, unary nodes in the compacted tree can be consumed later to introduce new attrs without unbalancing the tree. Now I'm wondering, how would a rebalancing algorithm even work on a red-black tree without unary nodes...? Did I dodge a bullet here? Unaligned compaction with coloring: tags: effective rby tree: data a <. .---> a data b <--. .---r-b-> b data c <----. | .---> c data d <------. .---r-b-r-b-> d data e <--------. r-b---b---b-> e altrle a <. | | | | altble b -|-' | | | null | | | | effective 2-3-4 tree: altrle c <--.-' | | .-o -. altble d -|-|---' | .---o o +- h=3 null | | | .-o .-o o | altble e <----.---' a b c d e -' null | | | altrle b <. | | altble d -|-' | null | | altble e <--.-' null | | altrle d -' | altble e ---' null Code changes minimal, just needed some twiddly logic in lfsr_rbyd_appendcompaction to make this work: code stack before: 34256 2864 after: 34288 (+0.1%) 2864 (+0.0%) --- lfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lfs.c b/lfs.c index 6bcd0bda..53281bd4 100644 --- a/lfs.c +++ b/lfs.c @@ -3734,8 +3734,9 @@ static int lfsr_rbyd_appendcompaction(lfs_t *lfs, lfsr_rbyd_t *rbyd, // ignore shrub trunks, unless we are actually compacting // a shrub tree - if (!lfsr_rbyd_isshrub(rbyd) - && lfsr_tag_isshrub(tag__)) { + if (!lfsr_tag_isalt(tag__) + && lfsr_tag_isshrub(tag__) + && !lfsr_rbyd_isshrub(rbyd)) { trunk = off; weight = 0; continue; @@ -3765,7 +3766,12 @@ static int lfsr_rbyd_appendcompaction(lfs_t *lfs, lfsr_rbyd_t *rbyd, // connect with an altle err = lfsr_rbyd_appendtag(lfs, rbyd, - LFSR_TAG_ALT(LFSR_TAG_LE, LFSR_TAG_B, tag), + LFSR_TAG_ALT( + LFSR_TAG_LE, + (i == 0 && off < layer_) + ? LFSR_TAG_R + : LFSR_TAG_B, + tag), weight, rbyd->eoff - trunk); if (err) {