Skip to content

Commit 12205f1

Browse files
author
Stjepan Glavina
committedJun 24, 2017
Improve sort tests and benchmarks
1 parent 7e76505 commit 12205f1

File tree

5 files changed

+180
-96
lines changed

5 files changed

+180
-96
lines changed
 

‎src/liballoc/benches/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![feature(sort_unstable)]
1818
#![feature(test)]
1919

20+
extern crate rand;
2021
extern crate test;
2122

2223
mod btree;

‎src/liballoc/benches/slice.rs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::{mem, ptr};
12-
use std::__rand::{Rng, thread_rng};
11+
use std::__rand::{thread_rng};
12+
use std::mem;
13+
use std::ptr;
1314

15+
use rand::{Rng, SeedableRng, XorShiftRng};
1416
use test::{Bencher, black_box};
1517

1618
#[bench]
@@ -191,17 +193,17 @@ fn gen_descending(len: usize) -> Vec<u64> {
191193
}
192194

193195
fn gen_random(len: usize) -> Vec<u64> {
194-
let mut rng = thread_rng();
196+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
195197
rng.gen_iter::<u64>().take(len).collect()
196198
}
197199

198200
fn gen_random_bytes(len: usize) -> Vec<u8> {
199-
let mut rng = thread_rng();
201+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
200202
rng.gen_iter::<u8>().take(len).collect()
201203
}
202204

203205
fn gen_mostly_ascending(len: usize) -> Vec<u64> {
204-
let mut rng = thread_rng();
206+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
205207
let mut v = gen_ascending(len);
206208
for _ in (0usize..).take_while(|x| x * x <= len) {
207209
let x = rng.gen::<usize>() % len;
@@ -212,7 +214,7 @@ fn gen_mostly_ascending(len: usize) -> Vec<u64> {
212214
}
213215

214216
fn gen_mostly_descending(len: usize) -> Vec<u64> {
215-
let mut rng = thread_rng();
217+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
216218
let mut v = gen_descending(len);
217219
for _ in (0usize..).take_while(|x| x * x <= len) {
218220
let x = rng.gen::<usize>() % len;
@@ -223,7 +225,7 @@ fn gen_mostly_descending(len: usize) -> Vec<u64> {
223225
}
224226

225227
fn gen_strings(len: usize) -> Vec<String> {
226-
let mut rng = thread_rng();
228+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
227229
let mut v = vec![];
228230
for _ in 0..len {
229231
let n = rng.gen::<usize>() % 20 + 1;
@@ -233,26 +235,40 @@ fn gen_strings(len: usize) -> Vec<String> {
233235
}
234236

235237
fn gen_big_random(len: usize) -> Vec<[u64; 16]> {
236-
let mut rng = thread_rng();
238+
let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
237239
rng.gen_iter().map(|x| [x; 16]).take(len).collect()
238240
}
239241

240242
macro_rules! sort {
241243
($f:ident, $name:ident, $gen:expr, $len:expr) => {
242244
#[bench]
243245
fn $name(b: &mut Bencher) {
244-
b.iter(|| $gen($len).$f());
246+
let v = $gen($len);
247+
b.iter(|| v.clone().$f());
245248
b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
246249
}
247250
}
248251
}
249252

253+
macro_rules! sort_strings {
254+
($f:ident, $name:ident, $gen:expr, $len:expr) => {
255+
#[bench]
256+
fn $name(b: &mut Bencher) {
257+
let v = $gen($len);
258+
let v = v.iter().map(|s| &**s).collect::<Vec<&str>>();
259+
b.iter(|| v.clone().$f());
260+
b.bytes = $len * mem::size_of::<&str>() as u64;
261+
}
262+
}
263+
}
264+
250265
macro_rules! sort_expensive {
251266
($f:ident, $name:ident, $gen:expr, $len:expr) => {
252267
#[bench]
253268
fn $name(b: &mut Bencher) {
269+
let v = $gen($len);
254270
b.iter(|| {
255-
let mut v = $gen($len);
271+
let mut v = v.clone();
256272
let mut count = 0;
257273
v.$f(|a: &u64, b: &u64| {
258274
count += 1;
@@ -263,38 +279,38 @@ macro_rules! sort_expensive {
263279
});
264280
black_box(count);
265281
});
266-
b.bytes = $len as u64 * mem::size_of::<u64>() as u64;
282+
b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
267283
}
268284
}
269285
}
270286

271287
sort!(sort, sort_small_ascending, gen_ascending, 10);
272288
sort!(sort, sort_small_descending, gen_descending, 10);
273289
sort!(sort, sort_small_random, gen_random, 10);
274-
sort!(sort, sort_small_big_random, gen_big_random, 10);
290+
sort!(sort, sort_small_big, gen_big_random, 10);
275291
sort!(sort, sort_medium_random, gen_random, 100);
276292
sort!(sort, sort_large_ascending, gen_ascending, 10000);
277293
sort!(sort, sort_large_descending, gen_descending, 10000);
278294
sort!(sort, sort_large_mostly_ascending, gen_mostly_ascending, 10000);
279295
sort!(sort, sort_large_mostly_descending, gen_mostly_descending, 10000);
280296
sort!(sort, sort_large_random, gen_random, 10000);
281-
sort!(sort, sort_large_big_random, gen_big_random, 10000);
282-
sort!(sort, sort_large_strings, gen_strings, 10000);
283-
sort_expensive!(sort_by, sort_large_random_expensive, gen_random, 10000);
297+
sort!(sort, sort_large_big, gen_big_random, 10000);
298+
sort_strings!(sort, sort_large_strings, gen_strings, 10000);
299+
sort_expensive!(sort_by, sort_large_expensive, gen_random, 10000);
284300

285301
sort!(sort_unstable, sort_unstable_small_ascending, gen_ascending, 10);
286302
sort!(sort_unstable, sort_unstable_small_descending, gen_descending, 10);
287303
sort!(sort_unstable, sort_unstable_small_random, gen_random, 10);
288-
sort!(sort_unstable, sort_unstable_small_big_random, gen_big_random, 10);
304+
sort!(sort_unstable, sort_unstable_small_big, gen_big_random, 10);
289305
sort!(sort_unstable, sort_unstable_medium_random, gen_random, 100);
290306
sort!(sort_unstable, sort_unstable_large_ascending, gen_ascending, 10000);
291307
sort!(sort_unstable, sort_unstable_large_descending, gen_descending, 10000);
292308
sort!(sort_unstable, sort_unstable_large_mostly_ascending, gen_mostly_ascending, 10000);
293309
sort!(sort_unstable, sort_unstable_large_mostly_descending, gen_mostly_descending, 10000);
294310
sort!(sort_unstable, sort_unstable_large_random, gen_random, 10000);
295-
sort!(sort_unstable, sort_unstable_large_big_random, gen_big_random, 10000);
296-
sort!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
297-
sort_expensive!(sort_unstable_by, sort_unstable_large_random_expensive, gen_random, 10000);
311+
sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000);
312+
sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
313+
sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000);
298314

299315
macro_rules! reverse {
300316
($name:ident, $ty:ty, $f:expr) => {

‎src/liballoc/slice.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,7 +1794,7 @@ unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
17941794

17951795
impl<T> Drop for MergeHole<T> {
17961796
fn drop(&mut self) {
1797-
// `T` is not a zero-sized type, so it's okay to divide by it's size.
1797+
// `T` is not a zero-sized type, so it's okay to divide by its size.
17981798
let len = (self.end as usize - self.start as usize) / mem::size_of::<T>();
17991799
unsafe { ptr::copy_nonoverlapping(self.start, self.dest, len); }
18001800
}
@@ -1908,7 +1908,7 @@ fn merge_sort<T, F>(v: &mut [T], mut is_less: F)
19081908
// if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
19091909
// algorithm should continue building a new run instead, `None` is returned.
19101910
//
1911-
// TimSort is infamous for it's buggy implementations, as described here:
1911+
// TimSort is infamous for its buggy implementations, as described here:
19121912
// http://envisage-project.eu/timsort-specification-and-verification/
19131913
//
19141914
// The gist of the story is: we must enforce the invariants on the top four runs on the stack.

‎src/liballoc/tests/slice.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -396,18 +396,44 @@ fn test_sort() {
396396
let mut rng = thread_rng();
397397

398398
for len in (2..25).chain(500..510) {
399-
for _ in 0..100 {
400-
let mut v: Vec<_> = rng.gen_iter::<i32>().take(len).collect();
401-
let mut v1 = v.clone();
402-
403-
v.sort();
404-
assert!(v.windows(2).all(|w| w[0] <= w[1]));
405-
406-
v1.sort_by(|a, b| a.cmp(b));
407-
assert!(v1.windows(2).all(|w| w[0] <= w[1]));
408-
409-
v1.sort_by(|a, b| b.cmp(a));
410-
assert!(v1.windows(2).all(|w| w[0] >= w[1]));
399+
for &modulus in &[5, 10, 100, 1000] {
400+
for _ in 0..10 {
401+
let orig: Vec<_> = rng.gen_iter::<i32>()
402+
.map(|x| x % modulus)
403+
.take(len)
404+
.collect();
405+
406+
// Sort in default order.
407+
let mut v = orig.clone();
408+
v.sort();
409+
assert!(v.windows(2).all(|w| w[0] <= w[1]));
410+
411+
// Sort in ascending order.
412+
let mut v = orig.clone();
413+
v.sort_by(|a, b| a.cmp(b));
414+
assert!(v.windows(2).all(|w| w[0] <= w[1]));
415+
416+
// Sort in descending order.
417+
let mut v = orig.clone();
418+
v.sort_by(|a, b| b.cmp(a));
419+
assert!(v.windows(2).all(|w| w[0] >= w[1]));
420+
421+
// Sort with many pre-sorted runs.
422+
let mut v = orig.clone();
423+
v.sort();
424+
v.reverse();
425+
for _ in 0..5 {
426+
let a = rng.gen::<usize>() % len;
427+
let b = rng.gen::<usize>() % len;
428+
if a < b {
429+
v[a..b].reverse();
430+
} else {
431+
v.swap(a, b);
432+
}
433+
}
434+
v.sort();
435+
assert!(v.windows(2).all(|w| w[0] <= w[1]));
436+
}
411437
}
412438
}
413439

‎src/test/run-pass/vector-sort-panic-safe.rs

Lines changed: 104 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010

1111
// ignore-emscripten no threads support
1212

13-
#![feature(rand)]
1413
#![feature(const_fn)]
14+
#![feature(rand)]
15+
#![feature(sort_unstable)]
1516

1617
use std::__rand::{thread_rng, Rng};
18+
use std::cell::Cell;
19+
use std::cmp::Ordering;
1720
use std::panic;
18-
use std::sync::atomic::{AtomicUsize, Ordering};
21+
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize};
22+
use std::sync::atomic::Ordering::Relaxed;
1923
use std::thread;
20-
use std::cell::Cell;
2124

2225
const MAX_LEN: usize = 80;
2326

@@ -45,54 +48,85 @@ static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [
4548
AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
4649
];
4750

48-
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
51+
static VERSIONS: AtomicUsize = ATOMIC_USIZE_INIT;
52+
53+
#[derive(Clone, Eq)]
4954
struct DropCounter {
5055
x: u32,
5156
id: usize,
57+
version: Cell<usize>,
58+
}
59+
60+
impl PartialEq for DropCounter {
61+
fn eq(&self, other: &Self) -> bool {
62+
self.partial_cmp(other) == Some(Ordering::Equal)
63+
}
64+
}
65+
66+
impl PartialOrd for DropCounter {
67+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
68+
self.version.set(self.version.get() + 1);
69+
other.version.set(other.version.get() + 1);
70+
VERSIONS.fetch_add(2, Relaxed);
71+
self.x.partial_cmp(&other.x)
72+
}
73+
}
74+
75+
impl Ord for DropCounter {
76+
fn cmp(&self, other: &Self) -> Ordering {
77+
self.partial_cmp(other).unwrap()
78+
}
5279
}
5380

5481
impl Drop for DropCounter {
5582
fn drop(&mut self) {
56-
DROP_COUNTS[self.id].fetch_add(1, Ordering::Relaxed);
83+
DROP_COUNTS[self.id].fetch_add(1, Relaxed);
84+
VERSIONS.fetch_sub(self.version.get(), Relaxed);
5785
}
5886
}
5987

60-
fn test(input: &[DropCounter]) {
61-
let len = input.len();
88+
macro_rules! test {
89+
($input:ident, $func:ident) => {
90+
let len = $input.len();
91+
92+
// Work out the total number of comparisons required to sort
93+
// this array...
94+
let mut count = 0usize;
95+
$input.to_owned().$func(|a, b| { count += 1; a.cmp(b) });
96+
97+
// ... and then panic on each and every single one.
98+
for panic_countdown in 0..count {
99+
// Refresh the counters.
100+
VERSIONS.store(0, Relaxed);
101+
for i in 0..len {
102+
DROP_COUNTS[i].store(0, Relaxed);
103+
}
62104

63-
// Work out the total number of comparisons required to sort
64-
// this array...
65-
let mut count = 0usize;
66-
input.to_owned().sort_by(|a, b| { count += 1; a.cmp(b) });
105+
let v = $input.to_owned();
106+
let _ = thread::spawn(move || {
107+
let mut v = v;
108+
let mut panic_countdown = panic_countdown;
109+
v.$func(|a, b| {
110+
if panic_countdown == 0 {
111+
SILENCE_PANIC.with(|s| s.set(true));
112+
panic!();
113+
}
114+
panic_countdown -= 1;
115+
a.cmp(b)
116+
})
117+
}).join();
118+
119+
// Check that the number of things dropped is exactly
120+
// what we expect (i.e. the contents of `v`).
121+
for (i, c) in DROP_COUNTS.iter().enumerate().take(len) {
122+
let count = c.load(Relaxed);
123+
assert!(count == 1,
124+
"found drop count == {} for i == {}, len == {}",
125+
count, i, len);
126+
}
67127

68-
// ... and then panic on each and every single one.
69-
for panic_countdown in 0..count {
70-
// Refresh the counters.
71-
for i in 0..len {
72-
DROP_COUNTS[i].store(0, Ordering::Relaxed);
73-
}
74-
75-
let v = input.to_owned();
76-
let _ = thread::spawn(move || {
77-
let mut v = v;
78-
let mut panic_countdown = panic_countdown;
79-
v.sort_by(|a, b| {
80-
if panic_countdown == 0 {
81-
SILENCE_PANIC.with(|s| s.set(true));
82-
panic!();
83-
}
84-
panic_countdown -= 1;
85-
a.cmp(b)
86-
})
87-
}).join();
88-
89-
// Check that the number of things dropped is exactly
90-
// what we expect (i.e. the contents of `v`).
91-
for (i, c) in DROP_COUNTS.iter().enumerate().take(len) {
92-
let count = c.load(Ordering::Relaxed);
93-
assert!(count == 1,
94-
"found drop count == {} for i == {}, len == {}",
95-
count, i, len);
128+
// Check that the most recent versions of values were dropped.
129+
assert_eq!(VERSIONS.load(Relaxed), 0);
96130
}
97131
}
98132
}
@@ -106,33 +140,40 @@ fn main() {
106140
prev(info);
107141
}
108142
}));
143+
109144
for len in (1..20).chain(70..MAX_LEN) {
110-
// Test on a random array.
111-
let mut rng = thread_rng();
112-
let input = (0..len).map(|id| {
113-
DropCounter {
114-
x: rng.next_u32(),
115-
id: id,
116-
}
117-
}).collect::<Vec<_>>();
118-
test(&input);
119-
120-
// Test on a sorted array with two elements randomly swapped, creating several natural
121-
// runs of random lengths. Such arrays have very high chances of hitting all code paths in
122-
// the merge procedure.
123-
for _ in 0..5 {
124-
let mut input = (0..len).map(|i|
125-
DropCounter {
126-
x: i as u32,
127-
id: i,
145+
for &modulus in &[5, 20, 50] {
146+
for &has_runs in &[false, true] {
147+
let mut rng = thread_rng();
148+
let mut input = (0..len)
149+
.map(|id| {
150+
DropCounter {
151+
x: rng.next_u32() % modulus,
152+
id: id,
153+
version: Cell::new(0),
154+
}
155+
})
156+
.collect::<Vec<_>>();
157+
158+
if has_runs {
159+
for c in &mut input {
160+
c.x = c.id as u32;
161+
}
162+
163+
for _ in 0..5 {
164+
let a = rng.gen::<usize>() % len;
165+
let b = rng.gen::<usize>() % len;
166+
if a < b {
167+
input[a..b].reverse();
168+
} else {
169+
input.swap(a, b);
170+
}
171+
}
128172
}
129-
).collect::<Vec<_>>();
130173

131-
let a = rng.gen::<usize>() % len;
132-
let b = rng.gen::<usize>() % len;
133-
input.swap(a, b);
134-
135-
test(&input);
174+
test!(input, sort_by);
175+
test!(input, sort_unstable_by);
176+
}
136177
}
137178
}
138179
}

0 commit comments

Comments
 (0)
Please sign in to comment.