Skip to content

Commit

Permalink
Auto merge of #29675 - bluss:merge-sort-fastpath, r=huonw
Browse files Browse the repository at this point in the history
sort: Fast path for already sorted data

When merging two sorted blocks `left` and `right` if the last element in
`left` is <= the first in `right`, the blocks are already in sorted order.

Add this as an additional fast path by simply copying the whole left
block into the output and advancing the left pointer. The right block is
then treated the same way by the already present logic in the merge
loop.

Can reduce runtime of .sort() to less than 50% of the previous, if the data
was already perfectly sorted. Sorted data with a few swaps are also
sorted quicker than before. The overhead of one comparison per merge
seems to be negligible.
  • Loading branch information
bors committed Nov 13, 2015
2 parents d5fde83 + 0f5e30d commit 8a813e0
Showing 1 changed file with 12 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/libcollections/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,18 @@ fn merge_sort<T, F>(v: &mut [T], mut compare: F) where F: FnMut(&T, &T) -> Order
let mut out = buf_tmp.offset(start as isize);
let out_end = buf_tmp.offset(right_end_idx as isize);

// If left[last] <= right[0], they are already in order:
// fast-forward the left side (the right side is handled
// in the loop).
// If `right` is not empty then left is not empty, and
// the offsets are in bounds.
if right != right_end && compare(&*right.offset(-1), &*right) != Greater {
let elems = (right_start as usize - left as usize) / mem::size_of::<T>();
ptr::copy_nonoverlapping(&*left, out, elems);
out = out.offset(elems as isize);
left = right_start;
}

while out < out_end {
// Either the left or the right run are exhausted,
// so just copy the remainder from the other run
Expand Down

0 comments on commit 8a813e0

Please sign in to comment.