Preserve coloring on range removals
This rearranges diverged pruning in lfsr_rbyd_appendattr a bit to try to avoid unnecessary r->b recoloring during range removals. Two tweaks: 1. lfsr_rbyd_appendattr now only alternates diverged paths on a black edge. This is equivalent to alternating on the underlying 2-3-4 tree, and means we don't have to worry about overlapping red edges from the two diverged paths interacting with each other in weird ways. 2. Thanks to alternating on a black edge, we can now prune diverged paths before applying our red-black-yellow operations. This means we can avoid the somewhat-hack that was r->b recoloring (if you paint it black, red-yellow operations are skipped so nothing breaks trivially, but you also break your tree balance invariants). So the two diverged paths should remain strictly 2(log n)+1, at least in isolation. A nice side-effect of moving the diverged-prune code is we can deduplicate pruning with yellow-edge prunning. This saves a branch of code, though it will makes things a bit more confusing if anyone tries to use lfsr_rbyd_appendattr as a template for an rbyd implementation without range removals. The new pruning conditions are more complex, resulting in more code cost, but this will be worth it if it results in better tree balance after range removals: before: 33912 2880 after: 34064 (+0.4%) 2880 (+0.0%) Some quick, non-rigorous benchmarks showed a noticable, but tiny improvement in random write performance. Though I'm not sure random writes are likely to hit degenerate range removals...
This commit is contained in:
@@ -2798,13 +2798,13 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
jump = branch - jump;
|
||||
lfs_size_t branch_ = branch + d;
|
||||
|
||||
// do bounds want to take different paths? begin cutting
|
||||
// do bounds want to take different paths? begin diverging
|
||||
if (!lfsr_tag_hasdiverged(tag_)
|
||||
&& lfsr_tag_follow2(alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
rid_, tag_)
|
||||
!= lfsr_tag_follow2(alt, weight,
|
||||
^ lfsr_tag_follow2(alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
other_rid_, other_tag_)) {
|
||||
@@ -2826,13 +2826,6 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
}
|
||||
}
|
||||
|
||||
// if we're diverging, go ahead and make alt black, this isn't
|
||||
// perfect but it's simpler and compact will take care of any
|
||||
// balance issues that may occur
|
||||
if (lfsr_tag_hasdiverged(tag_)) {
|
||||
alt &= ~LFSR_TAG_R;
|
||||
}
|
||||
|
||||
// prune?
|
||||
// <b >b
|
||||
// .-'| .-'|
|
||||
@@ -2847,7 +2840,32 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
lower_tag, upper_tag)) {
|
||||
lower_tag, upper_tag)
|
||||
// prune because of diverged paths?
|
||||
|| (lfsr_tag_hasdiverged(tag_)
|
||||
&& lfsr_tag_isdivergedupper(tag_)
|
||||
^ lfsr_tag_isgt(alt)
|
||||
^ lfsr_tag_follow2(
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
rid_, tag_))) {
|
||||
// note yellow prunes always follow and have no weight, it's
|
||||
// only the diverged paths that need all these special cases
|
||||
if (lfsr_tag_follow2(
|
||||
alt, weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid,
|
||||
rid_, tag_)) {
|
||||
lfsr_tag_flip2(
|
||||
&alt, &weight,
|
||||
p_alts[0], p_weights[0],
|
||||
lower_rid, upper_rid);
|
||||
lfsr_tag_trim(
|
||||
alt, weight,
|
||||
&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];
|
||||
@@ -2855,9 +2873,27 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
jump = p_jumps[0];
|
||||
lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
|
||||
} else {
|
||||
graft = branch;
|
||||
branch = jump;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
lfsr_tag_trim(
|
||||
alt, weight,
|
||||
&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];
|
||||
jump = p_jumps[0];
|
||||
lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
|
||||
} else {
|
||||
graft = branch;
|
||||
branch = branch_;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// two reds makes a yellow, split?
|
||||
@@ -2972,12 +3008,6 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
graft = branch;
|
||||
branch = branch_;
|
||||
|
||||
// prune inner alts if our tags diverged
|
||||
if (lfsr_tag_hasdiverged(tag_)
|
||||
&& lfsr_tag_isdivergedupper(tag_) != lfsr_tag_isgt(alt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// push alt onto our queue
|
||||
int err = lfsr_rbyd_p_push(lfs, rbyd,
|
||||
p_alts, p_weights, p_jumps,
|
||||
@@ -3002,7 +3032,9 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
||||
}
|
||||
|
||||
// switch to the other path if we have diverged
|
||||
if (lfsr_tag_hasdiverged(tag_) || !lfsr_tag_isalt(alt)) {
|
||||
if (!lfsr_tag_isalt(alt)
|
||||
|| (lfsr_tag_hasdiverged(tag_)
|
||||
&& lfsr_tag_isblack(p_alts[0]))) {
|
||||
lfs_swap16(&tag_, &other_tag_);
|
||||
lfs_sswap32(&rid_, &other_rid_);
|
||||
lfs_swap32(&branch, &other_branch);
|
||||
|
||||
Reference in New Issue
Block a user