Skip to content

Commit 75ccb01

Browse files
authored
Improve performance by optimizing bundle merging logic. (#204)
While merging two bundles, the `merge_bundles` function appends two sorted vectors and sort the resulting vector again. This approach used to be fast in most cases, but rustc 1.81 introduced changes in sorting algorithm that made `sort_unstable_by_key` to behave very bad with vectors that are almost sorted. This introduces an optimization consisting in handling the special case where the vector being appended contains a single item differently. This case is very common, and there's a benefit in handling it differently both with rust 1.81 and with earlier versions. Fixes #203
1 parent a3296d0 commit 75ccb01

File tree

2 files changed

+24
-10
lines changed

2 files changed

+24
-10
lines changed

deny.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
[graph]
12
targets = [
23
{ triple = "x86_64-unknown-linux-gnu" },
34
{ triple = "x86_64-apple-darwin" },
@@ -7,8 +8,6 @@ targets = [
78

89
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
910
[advisories]
10-
vulnerability = "deny"
11-
unmaintained = "deny"
1211
yanked = "deny"
1312
ignore = []
1413

@@ -19,6 +18,7 @@ allow = [
1918
"Apache-2.0",
2019
"MIT",
2120
"Unicode-DFS-2016",
21+
"Unicode-3.0",
2222
]
2323

2424
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html

src/ion/merge.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,33 @@ impl<'a, F: Function> Env<'a, F> {
170170
ranges_to
171171
);
172172

173-
// Two non-empty lists of LiveRanges: concatenate and
174-
// sort. This is faster than a mergesort-like merge into a new
175-
// list, empirically.
176173
let empty_vec = LiveRangeList::new_in(self.ctx.bump());
177-
let from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
174+
let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
178175
for entry in &from_list {
179176
self.ranges[entry.index].bundle = to;
180177
}
181178

182-
self.bundles[to].ranges.extend_from_slice(&from_list[..]);
183-
self.bundles[to]
184-
.ranges
185-
.sort_unstable_by_key(|entry| entry.range.from);
179+
if from_list.len() == 1 {
180+
// Optimize for the common case where `from_list` contains a single
181+
// item. Using a binary search to find the insertion point and then
182+
// calling `insert` is more efficient than re-sorting the entire
183+
// list, specially after the changes in sorting algorithms introduced
184+
// in rustc 1.81.
185+
// See: https://github.com/bytecodealliance/regalloc2/issues/203
186+
let single_entry = from_list.pop().unwrap();
187+
let pos = self.bundles[to]
188+
.ranges
189+
.binary_search_by_key(&single_entry.range.from, |entry| entry.range.from)
190+
.unwrap_or_else(|pos| pos);
191+
self.bundles[to].ranges.insert(pos, single_entry);
192+
} else {
193+
// Two non-empty lists of LiveRanges: concatenate and sort. This is
194+
// faster than a mergesort-like merge into a new list, empirically.
195+
self.bundles[to].ranges.extend_from_slice(&from_list[..]);
196+
self.bundles[to]
197+
.ranges
198+
.sort_unstable_by_key(|entry| entry.range.from);
199+
}
186200

187201
if self.annotations_enabled {
188202
trace!("merging: merged = {:?}", self.bundles[to].ranges);

0 commit comments

Comments
 (0)