From 35fd1bab5e727061248c7810ca1fbe81e336d019 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 7 Nov 2015 17:39:36 +0100 Subject: [PATCH 1/2] 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 sorted. 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. Reduces 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. --- src/libcollections/slice.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index ea4830fc3e6..cb3f39e0cac 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1066,6 +1066,16 @@ fn merge_sort(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 compare(&*right.offset(-1), &*right) != Greater { + let elems = (right_start as usize - left as usize) / mem::size_of::(); + 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 From 0f5e30d16004334cac1a25dbeefc0cfb886a404d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 7 Nov 2015 18:09:20 +0100 Subject: [PATCH 2/2] sort: Guard the fast path by length check The right part must not be empty. --- src/libcollections/slice.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index cb3f39e0cac..9ea3389e81c 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1066,10 +1066,12 @@ fn merge_sort(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: + // If left[last] <= right[0], they are already in order: // fast-forward the left side (the right side is handled // in the loop). - if compare(&*right.offset(-1), &*right) != Greater { + // 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::(); ptr::copy_nonoverlapping(&*left, out, elems); out = out.offset(elems as isize);