You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Auto merge of #3837 - JoJoDeveloping:tb-compacting-provenance-gc, r=RalfJung
Make Tree Borrows Provenance GC compact the tree
Follow-up on #3833 and #3835. In these PRs, the TB GC was fixed to no longer cause a stack overflow. One test that motivated it was the test `fill::horizontal_line` in [`tiny-skia`](https://github.com/RazrFalcon/tiny-skia). But not causing stack overflows was not a large improvents, since it did not fix the fundamental issue: The tree was too large. The test now ran, but it required gigabytes of memory and hours of time (only for it to be OOM-killed 🤬), whereas it finishes within 24 seconds in Stacked Borrows. With this merged, it finishes in about 40 seconds under TB.
The problem in that test was that it used [`slice::chunked`](https://doc.rust-lang.org/std/primitive.slice.html#method.chunks) to iterate a slice in chunks. That iterator is written to reborrow at each call to `next`, which creates a linear tree with a bunch of intermediary nodes, which also fragments the `RangeMap` for that allocation.
The solution is to now compact the tree, so that these interior nodes are removed. Care is taken to not remove nodes that are protected, or that otherwise restrict their children.
I am currently only 99% sure that this is sound, and I do also think that this could compact even more. So `@Vanille-N` please also have a look at whether I got the compacting logic right.
For a more visual comparison, [here is a gist](https://gist.github.com/JoJoDeveloping/ae4a7f7c29335a4c233ef42d2f267b01) of what the tree looks like at one point during that test, with and without compacting.
This new GC requires a different iteration order during accesses (since the current one can make the error messages non-deterministic), so it is rebased on top of #3843 and requires that PR to be merged first.
"`can_be_replaced_by_child` is not a simulation: on a {} {} to a {} parent and a {} {}{} child, the parent becomes {}, the child becomes {}, and these are not in simulation!",
102
+
as_foreign_or_child(rel),
103
+
kind,
104
+
parent.permission(),
105
+
as_lazy_or_init(child.is_initialized()),
106
+
child.permission(),
107
+
as_protected(child_protected),
108
+
np.permission(),
109
+
nc.permission()
110
+
)
111
+
}
112
+
(_,None) => {
113
+
// the child produced UB, this is fine no matter what the parent does
114
+
}
115
+
(None,Some(nc)) => {
116
+
panic!(
117
+
"`can_be_replaced_by_child` does not have the UB property: on a {} {} to a(n) {} parent and a(n) {} {}{} child, only the parent causes UB, while the child becomes {}, and it is not allowed for only the parent to cause UB!",
118
+
as_foreign_or_child(rel),
119
+
kind,
120
+
parent.permission(),
121
+
as_lazy_or_init(child.is_initialized()),
122
+
child.permission(),
123
+
as_protected(child_protected),
124
+
nc.permission()
125
+
)
126
+
}
127
+
}
128
+
}
129
+
}
130
+
}
131
+
67
132
#[test]
68
133
#[rustfmt::skip]
69
134
// Ensure that of 2 accesses happen, one foreign and one a child, and we are protected, that we
0 commit comments