Fix perf issue with many safepoints.

In wasmtime's `gc::many_live_refs` unit-test, approximately ~1K vregs
are live over ~1K safepoints (actually, each vreg is live over half the
safepoints on average, in a LIFO sort of arrangement).

This causes a huge slowdown with the current heuristics. Basically, each
vreg had a `Conflict` requirement because it had both stack uses
(safepoints) and register uses (the actual def and normal use). The
action in this case when processing the vreg's bundle is to split off
the first use -- a conservative-but-correct approach that will always
eventually split bundles far enough to get non-conflicting-requirement
pieces.

However, because each vreg had N stack uses followed by one register
use, this meant that each had to be split N times (!) -- so we had
O(n^2) splits and O(n^2) bundles by the end of the allocation.

This instead implements another simple heuristic that is much better:
when the requirements are conflicting, scan forward and find the exact
point at which the requirements become conflicting, such that the prefix
(first half prior to the split) still has no conflict, and split there.
This turns the above test-case into an O(n)-bundle / O(n)-split
situation.
This commit is contained in:
Chris Fallin
2021-06-22 14:06:59 -07:00
parent f27abc9c48
commit 66d6821c7b
2 changed files with 30 additions and 3 deletions

View File

@@ -955,9 +955,11 @@ impl<'a, F: Function> Env<'a, F> {
}
if self.func.is_safepoint(inst) {
log::debug!("inst{} is safepoint", inst.index());
self.safepoints.push(inst);
for vreg in live.iter() {
if let Some(safepoints) = self.safepoints_per_vreg.get_mut(&vreg) {
log::debug!("vreg v{} live at safepoint inst{}", vreg, inst.index());
safepoints.insert(inst);
}
}