1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
use ;
use ;
use thread;
use ;
use min_max;
use IntegerKey;
/// Sorts a slice of integers, `char`s or `bool`s by sleeping: one thread per element sleeps for
/// as long as its value says, and the values are written back in the order their threads wake.
/// Returns whether the slice came out sorted, which it may not have.
///
/// Sleep sort was posted anonymously to 4chan's programming board in 2011 as a shell script that
/// started `sleep $n; echo $n` in the background for every argument `n`: the small numbers wake
/// first and are printed first. It is a joke, and a good one: it sorts in "linear time", lets the
/// operating system's timer queue do all the work, and is wrong as soon as the scheduler is late.
/// This is that algorithm with real threads and real sleeping, generalised from non-negative
/// numbers to any [`IntegerKey`]: each element sleeps `(value − min) × unit` past a start all
/// threads share, so negative numbers, `char`s and `bool`s work too, and the smallest values
/// wake right at the start.
///
/// **It is nondeterministic and intended as a curiosity.** Nothing guarantees that a thread
/// meant to wake `unit` after another wakes after it: a thread scheduled late (a loaded machine,
/// a timer that fires late) lets a larger value overtake a smaller one, and the slice is then not
/// sorted. The return value says whether it is: `true` exactly when the slice ends up in order.
/// Whatever happens, the slice ends up a permutation of what it was: every value is written back
/// exactly once, to the next free place.
///
/// Four refinements make the realisation as faithful and as robust as real threads allow; the
/// shares of sorts that came out sorted below are for random keys in `0..n`, on a 16-core Mac
/// loaded by other benchmarks (load average 3–15), so they vary from run to run:
///
/// * **One shared start.** Every thread sleeps until `start + (value − min) × unit`, an instant
/// all threads share, not for a duration from whenever it happened to be started: the order
/// in which threads are started, at 15 µs each for 1000 of them, does not skew when they
/// wake. Sleeping from each thread's own start, 100 elements at 100 µs per unit and 1000
/// elements at 1 ms never came out sorted; from the shared start, almost always.
/// * **The clock starts once every thread is running, and a little later.** All threads are
/// started first and wait at a gate, a [`RwLock`] the caller holds for writing; then the
/// start is set and the gate opened. Waking the waiting threads takes the system 1.5–3 µs per
/// thread, so the start is set 4 µs per thread after the gate opens, by when every thread
/// has read it and gone to sleep: with the start at the gate, the threads of the smallest
/// values, released last, woke after the next ones, and 1000 elements at 100 µs never came
/// out sorted (100 elements: 64%; 1000 elements at 1 ms: 30%).
/// * **Sleeping in halves.** The system may let a sleep run longer than asked, to wake several
/// threads together: here a sleep of 1 ms took 1.5 ms, of 5 ms 7.5 ms, of 50 ms up to 60 ms,
/// so a value of 9 woke after a value of 12. Each thread sleeps for half the time left until
/// its deadline, again and again, and for all of it only once less than 50 µs is left: a
/// sleep that runs late still ends before the deadline, and the last one, being short, runs
/// late by a few dozen microseconds at most. At 1 ms per unit: from 39% to 100% sorted for 10
/// elements, from none to all for 100, and from none to 60–95% for 1000. Spinning through
/// the last microseconds instead of sleeping was no better, and worse with 1000 threads.
/// * **Small stacks.** Each thread gets a stack of 16 KiB instead of the default 2 MiB, which a
/// sleeping thread does not need: starting 5000 threads takes half as long.
///
/// The values are written back lock-free, each waking thread taking the next place from an
/// atomic counter; `(value − min) × unit` is computed in 128 bits and saturates at
/// [`Duration::MAX`], and whether the clock can represent the last deadline is checked before
/// any thread is started. If a thread cannot be started, the threads already waiting at the gate
/// are sent home without writing anything.
///
/// # Properties
///
/// * **Nondeterministic**: whether the result is sorted depends on the scheduler; it is
/// returned. Equal values wake together, in any order, which cannot be seen.
/// * **Time**: `(max − min) × unit`, plus starting `n` threads (about 15 µs each up to 1000
/// threads, more beyond: 0.27 s for 5000, 1 s for 10,000) and 4 µs per thread before the
/// start, whatever the order of the input: sleep sort is "linear" in the range of the values,
/// not in their number.
/// * **Not in place**: one thread per element, each with a 16 KiB stack: `O(n)` threads and
/// memory. A process may only have so many threads (16,384 on macOS).
///
/// The share of sorts that came out sorted on this machine, over three runs of 20–400 sorts
/// each (fewer for the longer sorts):
///
/// | `unit` | 10 elements | 100 elements | 1000 elements |
/// |----------|-------------|--------------|---------------|
/// | 10 µs | 12–18% | 0% | 0% |
/// | 100 µs | 94–100% | 94–100% | 89–100% |
/// | 1 ms | 99–100% | 89–100% | 60–90% |
///
/// A unit of 10 µs is below what the timers resolve: a thread wakes 10–15 µs after its deadline
/// at best. A longer sort is longer exposed to the other work of the machine, which can hold a
/// thread back by a millisecond or more: 1000 elements at 1 ms per unit take a second, and came
/// out sorted less often than at 100 µs, which take a tenth of that.
///
/// Against [`slice::sort_unstable`] it is, of course, hopeless: the standard library sorts 1000
/// random `u32`s in 3.5 µs, sleep sort at 100 µs per unit in about 115 ms, 30,000 times as long,
/// and 100 in 0.2 µs against 11 ms.
///
/// # Panics
///
/// Panics, leaving the slice as it was, if
///
/// * the clock cannot represent when the largest value would wake, `(max − min) × unit` from
/// now: about 292 billion years on Unix-like platforms, for example the whole range of `i128`
/// at a nanosecond per unit, or of `u64` at a second (a zero `unit` never panics);
/// * the system cannot start a thread for every element (on macOS, a process may have 16,384):
/// the threads already started are sent home and joined first.
///
/// A range the clock can represent is slept through, however long: the whole range of `i64` at a
/// nanosecond per unit takes 584 years.
///
/// # Examples
///
/// ```
/// use a::sort::sleep;
/// use std::time::Duration;
///
/// let mut v = [5, 1, 4, 2, 8, 0, 3, 9, 7, 6];
/// let sorted = sleep(&mut v, Duration::from_millis(2));
/// // Usually true, but the scheduler decides.
/// if sorted {
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
/// }
/// // Always a permutation of the input.
/// v.sort_unstable();
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// // Negative numbers sleep from the smallest one; a zero unit wakes everyone at once.
/// let mut w = [3, -2, i32::MIN, 7];
/// let sorted = sleep(&mut w, Duration::ZERO);
/// assert_eq!(sorted, w.is_sorted());
/// ```
///
/// A range the clock cannot represent panics:
///
/// ```should_panic
/// use a::sort::sleep;
/// use std::time::Duration;
///
/// let mut v = [i128::MIN, i128::MAX];
/// sleep(&mut v, Duration::from_nanos(1));
/// ```
/// The name the sort panics with.
const SORT: &str = "sleep sort";
/// The stack size of each thread: 16 KiB, the smallest the platforms allow (`std` rounds it up
/// where the minimum is larger). A thread that only waits, sleeps and writes one value needs a
/// few KiB. On macOS, starting threads with the smallest stacks is 2 times faster from about
/// 5000 threads on (0.27 s instead of 0.55 s for 5000, 1.0 s instead of 2.3 s for 10,000, as
/// even 20 KiB costs as much as the default 2 MiB), and as fast below.
const STACK: usize = 16 * 1024;
/// How long after opening the gate the clock starts, per thread. The gate wakes the waiting
/// threads one after the other, which took 1.5–2.9 µs per thread here (up to 2.9 ms for 1000):
/// a thread released after the start whose value is the smallest would wake after the next
/// ones. Starting the clock later by twice that leaves every thread time to read the start
/// and go to sleep: with a start right at the gate, 1000 elements at 1 ms per unit came out
/// sorted in about two of three sorts, almost always broken at the smallest values.
const LEAD_PER_THREAD: Duration = from_micros;
/// Below this much time left, [`sleep_until`] sleeps for all of it; above, for half of it.
const FINE: Duration = from_micros;
/// The slice the threads write their values back into, shared by all of them.
;
// SAFETY: the threads only write through the pointer with `Out::write`, each to a place of its
// own, and a `T` written from another thread is fine for a `T: Send`.
unsafe
/// `unit × times`, or [`Duration::MAX`] if that is longer: the product of a 128-bit distance and
/// a duration of up to `2^64` seconds needs more than 128 bits of nanoseconds.
/// Sleeps until `deadline`, in halves: while more than [`FINE`] is left, it sleeps for half of
/// what is left, and then for the rest.
///
/// [`thread::sleep`] sleeps at least as long as it is asked, but the system may let it sleep
/// longer to wake several threads together: on macOS, here, a sleep of 1 ms took 1.5 ms, one of
/// 5 ms took 7.5 ms, and one of 50 ms up to 60 ms; a value of 9 and a unit of 1 ms then woke
/// after a value of 12. Asked for half of what is left, a sleep that comes out as late as that
/// still ends before the deadline, and the next one is shorter, and so less late: it ends at
/// most a few dozen microseconds after its deadline, and a thread wakes about `log₂` of its delay
/// in units of [`FINE`] times. From 19% to 100% sorted for 10 elements at 1 ms per unit, from
/// none to all for 100 elements. The loop also covers a sleep clock that runs a little behind
/// [`Instant`]'s: no thread wakes before its deadline.