Skip to content

Commit bf61ad6

Browse files
authored
Merge pull request #4826 from andralex/6192-redivivus
Use medianOf 3 and 5 to estimate pivot
2 parents 6fe14f2 + 26f47b1 commit bf61ad6

File tree

1 file changed

+39
-35
lines changed

1 file changed

+39
-35
lines changed

std/algorithm/sorting.d

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,44 +1423,21 @@ unittest //Issue 16413 - @system comparison function
14231423

14241424
private size_t getPivot(alias less, Range)(Range r)
14251425
{
1426-
import std.algorithm.mutation : swapAt;
1427-
1428-
// This algorithm sorts the first, middle and last elements of r,
1429-
// then returns the index of the middle element. In effect, it uses the
1430-
// median-of-three heuristic.
1431-
1432-
alias pred = binaryFun!(less);
1433-
immutable len = r.length;
1434-
immutable size_t mid = len / 2;
1435-
immutable uint result = ((cast(uint) (pred(r[0], r[mid]))) << 2) |
1436-
((cast(uint) (pred(r[0], r[len - 1]))) << 1) |
1437-
(cast(uint) (pred(r[mid], r[len - 1])));
1438-
1439-
switch (result)
1426+
auto mid = r.length / 2;
1427+
if (r.length < 512)
14401428
{
1441-
case 0b001:
1442-
r.swapAt(0, len - 1);
1443-
r.swapAt(0, mid);
1444-
break;
1445-
case 0b110:
1446-
r.swapAt(mid, len - 1);
1447-
break;
1448-
case 0b011:
1449-
r.swapAt(0, mid);
1450-
break;
1451-
case 0b100:
1452-
r.swapAt(mid, len - 1);
1453-
r.swapAt(0, mid);
1454-
break;
1455-
case 0b000:
1456-
r.swapAt(0, len - 1);
1457-
break;
1458-
case 0b111:
1459-
break;
1460-
default:
1461-
assert(0);
1429+
if (r.length >= 32)
1430+
medianOf!less(r, size_t(0), mid, r.length - 1);
1431+
return mid;
14621432
}
14631433

1434+
// The plan here is to take the median of five by taking five elements in
1435+
// the array, segregate around their median, and return the position of the
1436+
// third. We choose first, mid, last, and two more in between those.
1437+
1438+
auto quarter = r.length / 4;
1439+
medianOf!less(r,
1440+
size_t(0), mid - quarter, mid, mid + quarter, r.length - 1);
14641441
return mid;
14651442
}
14661443

@@ -1770,6 +1747,33 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
17701747
assert(numbers.equal!isIdentical(sorted));
17711748
}
17721749

1750+
unittest
1751+
{
1752+
// Simple regression benchmark
1753+
import std.random, std.algorithm.iteration, std.algorithm.mutation;
1754+
Random rng;
1755+
int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array;
1756+
static uint comps;
1757+
static bool less(int a, int b) { ++comps; return a < b; }
1758+
sort!less(a); // random numbers
1759+
sort!less(a); // sorted ascending
1760+
a.reverse();
1761+
sort!less(a); // sorted descending
1762+
a[] = 0;
1763+
sort!less(a); // all equal
1764+
1765+
// This should get smaller with time. On occasion it may go larger, but only
1766+
// if there's thorough justification.
1767+
debug enum uint watermark = 1676280;
1768+
else enum uint watermark = 1676220;
1769+
1770+
import std.conv;
1771+
assert(comps <= watermark, text("You seem to have pessimized sort! ",
1772+
watermark, " < ", comps));
1773+
assert(comps >= watermark, text("You seem to have improved sort!",
1774+
" Please update watermark from ", watermark, " to ", comps));
1775+
}
1776+
17731777
@safe unittest
17741778
{
17751779
import std.algorithm.internal : rndstuff;

0 commit comments

Comments
 (0)