Skip to content

Commit c0ee7b2

Browse files
committed
Improve performance by optimizing bundle mergin logic.
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 are 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.
1 parent a3296d0 commit c0ee7b2

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

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)