]> git.lizzy.rs Git - rust.git/commitdiff
libcore: sort_unstable: improve randomization in break_patterns.
authorVadzim Dambrouski <pftbest@gmail.com>
Sat, 25 Mar 2017 22:01:54 +0000 (01:01 +0300)
committerVadzim Dambrouski <pftbest@gmail.com>
Sun, 26 Mar 2017 17:37:04 +0000 (20:37 +0300)
Select 3 random points instead of just 1.
Also the code now compiles on 16bit architectures.

src/libcore/slice/sort.rs

index 307e4974d9769f6a1762c6c32951b6e88866674d..3612ee65bca0dba78c5ad3fd35e51bbba3b3340e 100644 (file)
@@ -498,32 +498,40 @@ fn partition_equal<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> usize
 #[cold]
 fn break_patterns<T>(v: &mut [T]) {
     let len = v.len();
-
     if len >= 8 {
-        // A random number will be taken modulo this one. The modulus is a power of two so that we
-        // can simply take bitwise "and", thus avoiding costly CPU operations.
-        let modulus = (len / 4).next_power_of_two();
-        debug_assert!(modulus >= 1 && modulus <= len / 2);
-
-        // Pseudorandom number generation from the "Xorshift RNGs" paper by George Marsaglia.
-        let mut random = len;
-        random ^= random << 13;
-        random ^= random >> 17;
-        random ^= random << 5;
-        random &= modulus - 1;
-        debug_assert!(random < len / 2);
-
-        // The first index.
-        let a = len / 4 * 2;
-        debug_assert!(a >= 1 && a < len - 2);
-
-        // The second index.
-        let b = len / 4 + random;
-        debug_assert!(b >= 1 && b < len - 2);
-
-        // Swap neighbourhoods of `a` and `b`.
+        // Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia.
+        let mut random = len as u32;
+        let mut gen_u32 = || {
+            random ^= random << 13;
+            random ^= random >> 17;
+            random ^= random << 5;
+            random
+        };
+        let mut gen_usize = || {
+            if mem::size_of::<usize>() <= 4 {
+                gen_u32() as usize
+            } else {
+                (((gen_u32() as u64) << 32) | (gen_u32() as u64)) as usize
+            }
+        };
+
+        // Take random numbers modulo this number.
+        // The number fits into `usize` because `len` is not greater than `isize::MAX`.
+        let modulus = len.next_power_of_two();
+
+        // Some pivot candidates will be in the nearby of this index. Let's randomize them.
+        let pos = len / 4 * 2;
+
         for i in 0..3 {
-            v.swap(a - 1 + i, b - 1 + i);
+            // Generate a random number modulo `len`. However, in order to avoid costly operations
+            // we first take it modulo a power of two, and then decrease by `len` until it fits
+            // into the range `[0, len - 1]`.
+            let mut other = gen_usize() & (modulus - 1);
+            while other >= len {
+                other -= len;
+            }
+
+            v.swap(pos - 1 + i, other);
         }
     }
 }