about summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrik Sverdrup <bluss@users.noreply.github.com>2015-11-07 17:39:36 +0100
committerUlrik Sverdrup <bluss@users.noreply.github.com>2015-11-07 17:45:14 +0100
commit35fd1bab5e727061248c7810ca1fbe81e336d019 (patch)
tree7e0ec48d4a05927939f8b0e5759b922b7e345172
parent792a9f12cff83186a5426bc6e713fbc11261a4b1 (diff)
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.
-rw-r--r--src/libcollections/slice.rs10
1 files changed, 10 insertions, 0 deletions
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<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 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