Skip to content

Commit

Permalink
Improve performance by optimizing bundle merging logic. (#204)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
plusvic authored Dec 2, 2024
1 parent a3296d0 commit 75ccb01
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 10 deletions.
4 changes: 2 additions & 2 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[graph]
targets = [
{ triple = "x86_64-unknown-linux-gnu" },
{ triple = "x86_64-apple-darwin" },
Expand All @@ -7,8 +8,6 @@ targets = [

# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
vulnerability = "deny"
unmaintained = "deny"
yanked = "deny"
ignore = []

Expand All @@ -19,6 +18,7 @@ allow = [
"Apache-2.0",
"MIT",
"Unicode-DFS-2016",
"Unicode-3.0",
]

# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
Expand Down
30 changes: 22 additions & 8 deletions src/ion/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,33 @@ impl<'a, F: Function> Env<'a, F> {
ranges_to
);

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

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

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

0 comments on commit 75ccb01

Please sign in to comment.