Browse Source

Use David Musser's introsort algorithm to fall back to heapsort(3)

when the recursion depth reaches 2*lg(n + 1).  This avoids quicksort's
quadratic behavior for pathological input without appreciably
changing the average run time.
OPENBSD_6_2
millert 7 years ago
parent
commit
e83742917d
2 changed files with 45 additions and 14 deletions
  1. +2
    -1
      src/lib/libc/stdlib/heapsort.c
  2. +43
    -13
      src/lib/libc/stdlib/qsort.c

+ 2
- 1
src/lib/libc/stdlib/heapsort.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: heapsort.c,v 1.10 2016/10/22 19:19:34 tb Exp $ */
/* $OpenBSD: heapsort.c,v 1.11 2017/05/20 12:48:56 millert Exp $ */
/*- /*-
* Copyright (c) 1991, 1993 * Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -172,3 +172,4 @@ heapsort(void *vbase, size_t nmemb, size_t size,
free(k); free(k);
return (0); return (0);
} }
DEF_WEAK(heapsort);

+ 43
- 13
src/lib/libc/stdlib/qsort.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: qsort.c,v 1.15 2017/05/17 16:58:20 millert Exp $ */
/* $OpenBSD: qsort.c,v 1.16 2017/05/20 12:48:56 millert Exp $ */
/*- /*-
* Copyright (c) 1992, 1993 * Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -38,6 +38,18 @@ static __inline void swapfunc(char *, char *, size_t, int);
/* /*
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
*
* This version differs from Bentley & McIlroy in the following ways:
* 1. The partition value is swapped into a[0] instead of being
* stored out of line.
*
* 2. It uses David Musser's introsort algorithm to fall back to
* heapsort(3) when the recursion depth reaches 2*lg(n + 1).
* This avoids quicksort's quadratic behavior for pathological
* input without appreciably changing the average run time.
*
* 3. Tail recursion is eliminated when sorting the larger of two
* subpartitions to save stack space.
*/ */
#define swapcode(TYPE, parmi, parmj, n) { \ #define swapcode(TYPE, parmi, parmj, n) { \
size_t i = (n) / sizeof (TYPE); \ size_t i = (n) / sizeof (TYPE); \
@ -80,15 +92,20 @@ med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
} }
void
qsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *))
static void
introsort(char *a, size_t n, size_t es, size_t maxdepth,
int (*cmp)(const void *, const void *))
{ {
char *pa, *pb, *pc, *pd, *pl, *pm, *pn; char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
int cmp_result, swaptype; int cmp_result, swaptype;
size_t d, r, s;
char *a = aa;
size_t r, s;
loop: SWAPINIT(a, es);
loop: if (maxdepth == 0) {
if (heapsort(a, n, es, cmp) == 0)
return;
}
maxdepth--;
SWAPINIT(a, es);
if (n < 7) { if (n < 7) {
for (pm = a + es; pm < a + n * es; pm += es) for (pm = a + es; pm < a + n * es; pm += es)
for (pl = pm; pl > a && cmp(pl - es, pl) > 0; for (pl = pm; pl > a && cmp(pl - es, pl) > 0;
@ -101,16 +118,15 @@ loop: SWAPINIT(a, es);
pl = a; pl = a;
pn = a + (n - 1) * es; pn = a + (n - 1) * es;
if (n > 40) { if (n > 40) {
d = (n / 8) * es;
pl = med3(pl, pl + d, pl + 2 * d, cmp);
pm = med3(pm - d, pm, pm + d, cmp);
pn = med3(pn - 2 * d, pn - d, pn, cmp);
s = (n / 8) * es;
pl = med3(pl, pl + s, pl + 2 * s, cmp);
pm = med3(pm - s, pm, pm + s, cmp);
pn = med3(pn - 2 * s, pn - s, pn, cmp);
} }
pm = med3(pl, pm, pn, cmp); pm = med3(pl, pm, pn, cmp);
} }
swap(a, pm); swap(a, pm);
pa = pb = a + es; pa = pb = a + es;
pc = pd = a + (n - 1) * es; pc = pd = a + (n - 1) * es;
for (;;) { for (;;) {
while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) { while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {
@ -149,7 +165,7 @@ loop: SWAPINIT(a, es);
/* Recurse for 1st side, iterate for 2nd side. */ /* Recurse for 1st side, iterate for 2nd side. */
if (s > es) { if (s > es) {
if (r > es) if (r > es)
qsort(a, r / es, es, cmp);
introsort(a, r / es, es, maxdepth, cmp);
a = pn - s; a = pn - s;
n = s / es; n = s / es;
goto loop; goto loop;
@ -158,10 +174,24 @@ loop: SWAPINIT(a, es);
/* Recurse for 2nd side, iterate for 1st side. */ /* Recurse for 2nd side, iterate for 1st side. */
if (r > es) { if (r > es) {
if (s > es) if (s > es)
qsort(pn - s, s / es, es, cmp);
introsort(pn - s, s / es, es, maxdepth, cmp);
n = r / es; n = r / es;
goto loop; goto loop;
} }
} }
} }
void
qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *))
{
size_t i, maxdepth = 0;
/* Approximate 2*ceil(lg(n + 1)) */
for (i = n; i > 0; i >>= 1)
maxdepth++;
maxdepth *= 2;
introsort(a, n, es, maxdepth, cmp);
}
DEF_STRONG(qsort); DEF_STRONG(qsort);

Loading…
Cancel
Save