Preserve coloring during range removals
This is the real kicker of our new-and-improved range removal algorithm.
We can actually preserve the existing tree coloring, and the underlying
rbyd invariants.
Well, sort of. We preserve the red-follows-yellow and black-follows-red
rules, but we don't (can't?) preserve the same-height for all black
edges property.
But note! The new range removal algorithm never creates _new_ black
edges. It can only delete black edges, and otherwise preserves the
structure of the underlying 2-3-4 tree.
This means that while the resulting tree may not be perfectly balanced
with h=2*log2n', where n' is the _new_ number of tags, the resulting tree
_is_ limited to h=2*log2(n) where n is the _old_ number of tags.
When applied to our bounded rbyd, with eventual compaction and
rebalancing, we end up with the guarantee that the rbyd's height will
never exceed h=2*log2(b) where b is the block size, even with arbitrary
range removals.
This is a great result!
---
Note that this algorithm does not suffer from the yellow-diverge-yellow
corner case that was an issue for preserving coloring in the previous
one-pass stitching algorithm. This is because the one-pass algorithm
effectively deleted the diverging alt, breaking the tail-recursive
invariant of the underlying 2-3-4 tree. With the new two-pass algorithm,
we _replace_ the diverging alt with a black stitching alt to stitch
together the diverging trunks, so no tail-recursive invariant breaking.
(Also note even if we could preserve coloring in the one-pass algorithm,
it would still be breaking invariants by introducing new black edges
when it stitches together diverging trunks. Worst case, resulting in ~2x
the height, even when stitching with red alts (The red alt stitching
brings this cost down from ~4x to ~2x worst case due to blanket
recoloring. With yellow alt stitching this could probably be brought
down to ~1.3x, but this would still mean every range removal could be
increasing the height of the tree, which is not great.).)
---
Pruning has to be a bit more complicated now, since we need to be able
to recolor skipped red alts. But other than pruning the cost of
recoloring vs not recoloring is pretty small:
code stack
one-pass, blanket recolor: 33852 2880
two-pass, blanket recolor: 33860 (+0.0%) 2880 (+0.0%)
two-pass, color preserving: 33880 (+0.1%) 2880 (+0.0%)
The non-rigorous random-file-write benchmark I've been using as a litmus
test did not really show any improvements, but in hindsight it might
have been a bit silly to use a uniform distribution of writes to test
for rbyd balancing issues... Building a tree from a uniform distribution
already results in a balanced tree without doing anything!
This commit is contained in:
@@ -2884,10 +2884,7 @@ again:;
|
||||
if (d_state == LFSR_D_DIVERGEDUPPER && d_tag) {
|
||||
err = lfsr_rbyd_p_push(lfs, rbyd,
|
||||
p_alts, p_weights, p_jumps,
|
||||
LFSR_TAG_ALT(
|
||||
LFSR_TAG_LE,
|
||||
LFSR_TAG_B,
|
||||
d_tag),
|
||||
LFSR_TAG_ALT(LFSR_TAG_LE, LFSR_TAG_B, d_tag),
|
||||
d_rid - lower_rid,
|
||||
d_branch);
|
||||
if (err) {
|
||||
@@ -2897,13 +2894,23 @@ again:;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO rm me
|
||||
if (lfsr_d_isdiverged(d_state)) {
|
||||
alt &= ~LFSR_TAG_R;
|
||||
}
|
||||
|
||||
// prune diverged?
|
||||
if (d_state == LFSR_D_DIVERGINGLOWER
|
||||
// prune?
|
||||
// <b >b
|
||||
// .-'| .-'|
|
||||
// <y | | |
|
||||
// .-------'| | | |
|
||||
// | <r | => | <b
|
||||
// | .----' | .-----------|-'|
|
||||
// | | <b | <b |
|
||||
// | | .----'| | .----'| |
|
||||
// 1 2 3 4 4 1 2 3 4 4 2
|
||||
if (lfsr_tag_prune2(
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
lower_tag, upper_tag)
|
||||
// prune because of diverged paths?
|
||||
|| d_state == LFSR_D_DIVERGINGLOWER
|
||||
|| (lfsr_d_isdiverged(d_state)
|
||||
&& (d_state == LFSR_D_DIVERGEDUPPER)
|
||||
^ lfsr_tag_isgt(alt)
|
||||
@@ -2912,6 +2919,8 @@ again:;
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
a_rid, a_tag))) {
|
||||
// note, yellow prunes always follow and have no weight, it's
|
||||
// only diverged pruning that needs all these special cases
|
||||
if (lfsr_tag_follow2(
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
@@ -2928,35 +2937,14 @@ again:;
|
||||
&lower_rid, &upper_rid,
|
||||
&lower_tag, &upper_tag);
|
||||
|
||||
y_branch = branch;
|
||||
branch = branch_;
|
||||
continue;
|
||||
}
|
||||
|
||||
// prune?
|
||||
// <b >b
|
||||
// .-'| .-'|
|
||||
// <y | | |
|
||||
// .-------'| | | |
|
||||
// | <r | => | <b
|
||||
// | .----' | .-----------|-'|
|
||||
// | | <b | <b |
|
||||
// | | .----'| | .----'| |
|
||||
// 1 2 3 4 4 1 2 3 4 4 2
|
||||
if (lfsr_tag_prune2(
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
lower_tag, upper_tag)) {
|
||||
if (lfsr_tag_isred(p_alts[0])) {
|
||||
alt = p_alts[0] & ~LFSR_TAG_R;
|
||||
weight = p_weights[0];
|
||||
branch_ = jump;
|
||||
jump = p_jumps[0];
|
||||
lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
|
||||
} else {
|
||||
y_branch = branch;
|
||||
branch = jump;
|
||||
y_branch= branch;
|
||||
branch = branch_;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -3172,7 +3160,6 @@ again:;
|
||||
// split less than
|
||||
alt = LFSR_TAG_ALT(
|
||||
LFSR_TAG_LE,
|
||||
// TODO should this always be red?
|
||||
(!lfsr_d_isdiverged(d_state))
|
||||
? LFSR_TAG_R
|
||||
: LFSR_TAG_B,
|
||||
@@ -3199,7 +3186,6 @@ again:;
|
||||
// split greater than
|
||||
alt = LFSR_TAG_ALT(
|
||||
LFSR_TAG_GT,
|
||||
// TODO should this always be red?
|
||||
(!lfsr_d_isdiverged(d_state))
|
||||
? LFSR_TAG_R
|
||||
: LFSR_TAG_B,
|
||||
|
||||
Reference in New Issue
Block a user