Skip to content

Commit fda8e15

Browse files
committed
libcore: sort_unstable: improve randomization in break_patterns.
Select 3 random points instead of just 1. Also the code now compiles on 16bit architectures.
1 parent 7846dbe commit fda8e15

File tree

1 file changed

+32
-24
lines changed

1 file changed

+32
-24
lines changed

src/libcore/slice/sort.rs

+32-24
Original file line numberDiff line numberDiff line change
@@ -498,32 +498,40 @@ fn partition_equal<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> usize
498498
#[cold]
499499
fn break_patterns<T>(v: &mut [T]) {
500500
let len = v.len();
501-
502501
if len >= 8 {
503-
// A random number will be taken modulo this one. The modulus is a power of two so that we
504-
// can simply take bitwise "and", thus avoiding costly CPU operations.
505-
let modulus = (len / 4).next_power_of_two();
506-
debug_assert!(modulus >= 1 && modulus <= len / 2);
507-
508-
// Pseudorandom number generation from the "Xorshift RNGs" paper by George Marsaglia.
509-
let mut random = len;
510-
random ^= random << 13;
511-
random ^= random >> 17;
512-
random ^= random << 5;
513-
random &= modulus - 1;
514-
debug_assert!(random < len / 2);
515-
516-
// The first index.
517-
let a = len / 4 * 2;
518-
debug_assert!(a >= 1 && a < len - 2);
519-
520-
// The second index.
521-
let b = len / 4 + random;
522-
debug_assert!(b >= 1 && b < len - 2);
523-
524-
// Swap neighbourhoods of `a` and `b`.
502+
// Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia.
503+
let mut random = len as u32;
504+
let mut gen_u32 = || {
505+
random ^= random << 13;
506+
random ^= random >> 17;
507+
random ^= random << 5;
508+
random
509+
};
510+
let mut gen_usize = || {
511+
if mem::size_of::<usize>() <= 4 {
512+
gen_u32() as usize
513+
} else {
514+
(((gen_u32() as u64) << 32) | (gen_u32() as u64)) as usize
515+
}
516+
};
517+
518+
// Take random numbers modulo this number.
519+
// The number fits into `usize` because `len` is not greater than `isize::MAX`.
520+
let modulus = len.next_power_of_two();
521+
522+
// Some pivot candidates will be in the nearby of this index. Let's randomize them.
523+
let pos = len / 4 * 2;
524+
525525
for i in 0..3 {
526-
v.swap(a - 1 + i, b - 1 + i);
526+
// Generate a random number modulo `len`. However, in order to avoid costly operations
527+
// we first take it modulo a power of two, and then decrease by `len` until it fits
528+
// into the range `[0, len - 1]`.
529+
let mut other = gen_usize() & (modulus - 1);
530+
while other >= len {
531+
other -= len;
532+
}
533+
534+
v.swap(pos - 1 + i, other);
527535
}
528536
}
529537
}

0 commit comments

Comments
 (0)