diff --git a/src/backend/utils/sort/gen_qsort_tuple.pl b/src/backend/utils/sort/gen_qsort_tuple.pl index 6186d0a..a9ae496 100644 --- a/src/backend/utils/sort/gen_qsort_tuple.pl +++ b/src/backend/utils/sort/gen_qsort_tuple.pl @@ -108,6 +108,8 @@ sub emit_qsort_boilerplate * worth the effort", but we have seen crashes in the field due to stack * overrun, so that judgment seems wrong. */ + +#include static void swapfunc(SortTuple *a, SortTuple *b, size_t n) @@ -145,8 +147,63 @@ med3_$SUFFIX(SortTuple *a, SortTuple *b, SortTuple *c$EXTRAARGS) (cmp_$SUFFIX(a, c$CMPPARAMS) < 0 ? a : c)); } +/* heap sort: based on wikipedia */ + +static inline void +heap_shift_down_$SUFFIX(SortTuple *a, const size_t start, const size_t end$EXTRAARGS) { + size_t child, root = start; + + while ((root << 1) + 1 <= end) { + CHECK_FOR_INTERRUPTS(); + + child = (root << 1) + 1; + + if ((child < end) && cmp_$SUFFIX(a + child, a + (child + 1)$CMPPARAMS) < 0) { + child++; + } + + if (cmp_$SUFFIX(a + root, a + child$CMPPARAMS) < 0) { + swap(a + root, a + child); + root = child; + } + else { + return; + } + } +} + +static inline void +heap_sort_$SUFFIX(SortTuple *a, const size_t size$EXTRAARGS) { + + size_t start, end; + /* don't bother sorting an array of size <= 1 */ + if (size <= 1) { + return; + } + + end = size - 1; + + /* heapify */ + start = (end - 1) >> 1; + while (1) { + heap_shift_down_$SUFFIX(a, start, end$EXTRAPARAMS); + + if (start == 0) { + break; + } + + start--; + } + + while (end > 0) { + swap(a + end, a); + heap_shift_down_$SUFFIX(a, 0, end - 1$EXTRAPARAMS); + end--; + } +} + static void -qsort_$SUFFIX(SortTuple *a, size_t n$EXTRAARGS) +qsort_recursive_$SUFFIX(SortTuple *a, size_t n, size_t depth$EXTRAARGS) { SortTuple *pa, *pb, @@ -158,7 +215,7 @@ qsort_$SUFFIX(SortTuple *a, size_t n$EXTRAARGS) size_t d1, d2; int r, - presorted; + presorted; loop: CHECK_FOR_INTERRUPTS(); @@ -169,8 +226,9 @@ loop: swap(pl, pl - 1); return; } - presorted = 1; - for (pm = a + 1; pm < a + n; pm++) + + presorted = 1; + for (pm = a + 1; pm < a + n; pm++) { CHECK_FOR_INTERRUPTS(); if (cmp_$SUFFIX(pm - 1, pm$CMPPARAMS) > 0) @@ -181,6 +239,13 @@ loop: } if (presorted) return; + + /* convert to heap sort if exceed depth limit */ + if (!depth) { + heap_sort_$SUFFIX(a, n$EXTRAPARAMS); + return; + } + pm = a + (n / 2); if (n > 7) { @@ -238,13 +303,14 @@ loop: { /* Recurse on left partition, then iterate on right partition */ if (d1 > 1) - qsort_$SUFFIX(a, d1$EXTRAPARAMS); + qsort_recursive_$SUFFIX(a, d1, depth - 1$EXTRAPARAMS); if (d2 > 1) { /* Iterate rather than recurse to save stack space */ - /* qsort_$SUFFIX(pn - d2, d2$EXTRAPARAMS); */ + /* qsort_recursive_$SUFFIX(pn - d2, d2, depth - 1$EXTRAPARAMS); */ a = pn - d2; n = d2; + depth--; goto loop; } } @@ -252,15 +318,23 @@ loop: { /* Recurse on right partition, then iterate on left partition */ if (d2 > 1) - qsort_$SUFFIX(pn - d2, d2$EXTRAPARAMS); + qsort_recursive_$SUFFIX(pn - d2, d2, depth - 1$EXTRAPARAMS); if (d1 > 1) { /* Iterate rather than recurse to save stack space */ - /* qsort_$SUFFIX(a, d1$EXTRAPARAMS); */ + /* qsort_recursive_$SUFFIX(a, d1, depth - 1$EXTRAPARAMS); */ n = d1; + depth--; goto loop; } } } + +static void +qsort_$SUFFIX(SortTuple *a, size_t n$EXTRAARGS) +{ + qsort_recursive_$SUFFIX(a, n, 2 * log(n)$EXTRAPARAMS); +} + EOM } diff --git a/src/port/qsort.c b/src/port/qsort.c index 1a8ee08..49175a7 100644 --- a/src/port/qsort.c +++ b/src/port/qsort.c @@ -44,6 +44,8 @@ * SUCH DAMAGE. */ + +#include #include "c.h" @@ -109,32 +111,85 @@ med3(char *a, char *b, char *c, int (*cmp) (const void *, const void *)) : (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c)); } -void -pg_qsort(void *a, size_t n, size_t es, int (*cmp) (const void *, const void *)) +/* heap sort: based on wikipedia */ + +static inline void +heap_shift_down(void *a, const size_t start, const size_t end, + int swaptype, const size_t es, int(*cmp) (const void *, const void *)) { + size_t root = start; + + while ((root << 1) + 1 <= end) { + size_t child = (root << 1) + 1; + + if ((child < end) && cmp((char*)a + child * es, (char*)a + (child + 1)*es) < 0) { + child++; + } + + if (cmp((char*)a + root * es, (char*)a + child * es) < 0) { + swap((char*)a + root * es, (char*)a + child * es); + root = child; + } + else { + return; + } + } +} + +static inline void +heap_sort(void *a, const size_t size, int swaptype, const size_t es, int(*cmp) (const void *, const void *)) { + + size_t start, end; + /* don't bother sorting an array of size <= 1 */ + if (size <= 1) { + return; + } + + end = size - 1; + + /* heapify */ + start = (end - 1) >> 1; + while (1) { + heap_shift_down(a, start, end, swaptype, es, cmp); + + if (start == 0) { + break; + } + + start--; + } + + while (end > 0) { + swap((char*)a + end * es, (char*)a); + heap_shift_down(a, 0, end - 1, swaptype, es, cmp); + end--; + } +} + +static void +pg_qsort_recursive(void *a, size_t n, size_t depth, int swaptype, size_t es, int(*cmp) (const void *, const void *)) { char *pa, - *pb, - *pc, - *pd, - *pl, - *pm, - *pn; + *pb, + *pc, + *pd, + *pl, + *pm, + *pn; size_t d1, - d2; + d2; int r, - swaptype, - presorted; + presorted; -loop:SWAPINIT(a, es); +loop: if (n < 7) { - for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; - pl -= es) + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl >(char *) a && cmp(pl - es, pl) > 0; + pl -= es) swap(pl, pl - es); return; } - presorted = 1; + presorted = 1; for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) { if (cmp(pm - es, pm) > 0) @@ -145,11 +200,18 @@ loop:SWAPINIT(a, es); } if (presorted) return; - pm = (char *) a + (n / 2) * es; + + /* convert to heap sort if exceed depth limit */ + if (!depth) { + heap_sort(a, n, swaptype, es, cmp); + return; + } + + pm = (char *)a + (n / 2) * es; if (n > 7) { - pl = (char *) a; - pn = (char *) a + (n - 1) * es; + pl = (char *)a; + pn = (char *)a + (n - 1) * es; if (n > 40) { size_t d = (n / 8) * es; @@ -161,8 +223,8 @@ loop:SWAPINIT(a, es); pm = med3(pl, pm, pn, cmp); } swap(a, pm); - pa = pb = (char *) a + es; - pc = pd = (char *) a + (n - 1) * es; + pa = pb = (char *)a + es; + pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) @@ -189,8 +251,8 @@ loop:SWAPINIT(a, es); pb += es; pc -= es; } - pn = (char *) a + n * es; - d1 = Min(pa - (char *) a, pb - pa); + pn = (char *)a + n * es; + d1 = Min(pa - (char *)a, pb - pa); vecswap(a, pb - d1, d1); d1 = Min(pd - pc, pn - pd - es); vecswap(pb, pn - d1, d1); @@ -200,13 +262,14 @@ loop:SWAPINIT(a, es); { /* Recurse on left partition, then iterate on right partition */ if (d1 > es) - pg_qsort(a, d1 / es, es, cmp); + pg_qsort_recursive(a, d1 / es, depth - 1, swaptype, es, cmp); if (d2 > es) { /* Iterate rather than recurse to save stack space */ - /* pg_qsort(pn - d2, d2 / es, es, cmp); */ + /* pg_qsort_recursive(pn - d2, d2 / es, depth - 1, es, cmp); */ a = pn - d2; n = d2 / es; + depth--; goto loop; } } @@ -214,17 +277,26 @@ loop:SWAPINIT(a, es); { /* Recurse on right partition, then iterate on left partition */ if (d2 > es) - pg_qsort(pn - d2, d2 / es, es, cmp); + pg_qsort_recursive(pn - d2, d2 / es, depth - 1, swaptype, es, cmp); if (d1 > es) { /* Iterate rather than recurse to save stack space */ - /* pg_qsort(a, d1 / es, es, cmp); */ + /* pg_qsort_recursive(a, d1 / es, depth - 1, es, cmp); */ n = d1 / es; + depth--; goto loop; } } } + +void +pg_qsort(void *a, const size_t size, const size_t es, int(*cmp) (const void *, const void *)) { + int swaptype; + SWAPINIT(a, es); + pg_qsort_recursive(a, size, 2 * log(size), swaptype, es, cmp); +}; + /* * qsort comparator wrapper for strcmp. */ diff --git a/src/port/qsort_arg.c b/src/port/qsort_arg.c index 24acd2c..b785778 100644 --- a/src/port/qsort_arg.c +++ b/src/port/qsort_arg.c @@ -44,6 +44,8 @@ * SUCH DAMAGE. */ + +#include #include "c.h" @@ -102,39 +104,94 @@ swapfunc(char *a, char *b, size_t n, int swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) static char * -med3(char *a, char *b, char *c, qsort_arg_comparator cmp, void *arg) +med3(char *a, char *b, char *c, qsort_arg_comparator cmp, void* arg) { return cmp(a, b, arg) < 0 ? - (cmp(b, c, arg) < 0 ? b : (cmp(a, c, arg) < 0 ? c : a)) - : (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c)); + (cmp(b, c , arg) < 0 ? b : (cmp(a, c, arg) < 0 ? c : a)) + : (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c)); } -void -qsort_arg(void *a, size_t n, size_t es, qsort_arg_comparator cmp, void *arg) + +/* heap sort: based on wikipedia */ + +static inline void +heap_shift_down(void *a, const size_t start, const size_t end, + int swaptype, const size_t es, qsort_arg_comparator cmp, void* arg) { + size_t root = start; + + while ((root << 1) + 1 <= end) { + size_t child = (root << 1) + 1; + + if ((child < end) && cmp((char*)a + child * es, (char*)a + (child + 1)*es, arg) < 0) { + child++; + } + + if (cmp((char*)a + root * es, (char*)a + child * es, arg) < 0) { + swap((char*)a + root * es, (char*)a + child * es); + root = child; + } + else { + return; + } + } +} + +static inline void +heap_sort(void *a, const size_t size, int swaptype, const size_t es, qsort_arg_comparator cmp, void* arg) { + + size_t start, end; + /* don't bother sorting an array of size <= 1 */ + if (size <= 1) { + return; + } + + end = size - 1; + + /* heapify */ + start = (end - 1) >> 1; + while (1) { + heap_shift_down(a, start, end, swaptype, es, cmp, arg); + + if (start == 0) { + break; + } + + start--; + } + + while (end > 0) { + swap((char*)a + end * es, (char*)a); + heap_shift_down(a, 0, end - 1, swaptype, es, cmp, arg); + end--; + } +} + +static void +pg_qsort_arg_recursive(void *a, size_t n, size_t depth, int swaptype, size_t es, qsort_arg_comparator cmp, void* arg) { char *pa, - *pb, - *pc, - *pd, - *pl, - *pm, - *pn; + *pb, + *pc, + *pd, + *pl, + *pm, + *pn; size_t d1, - d2; + d2; int r, - swaptype, - presorted; + presorted; -loop:SWAPINIT(a, es); +loop: if (n < 7) { - for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl, arg) > 0; - pl -= es) + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl >(char *) a && cmp(pl - es, pl, arg) > 0; + pl -= es) swap(pl, pl - es); return; } - presorted = 1; + + presorted = 1; for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) { if (cmp(pm - es, pm, arg) > 0) @@ -145,11 +202,18 @@ loop:SWAPINIT(a, es); } if (presorted) return; - pm = (char *) a + (n / 2) * es; + + /* convert to heap sort if exceed depth limit */ + if (!depth) { + heap_sort(a, n, swaptype, es, cmp, arg); + return; + } + + pm = (char *)a + (n / 2) * es; if (n > 7) { - pl = (char *) a; - pn = (char *) a + (n - 1) * es; + pl = (char *)a; + pn = (char *)a + (n - 1) * es; if (n > 40) { size_t d = (n / 8) * es; @@ -161,8 +225,8 @@ loop:SWAPINIT(a, es); pm = med3(pl, pm, pn, cmp, arg); } swap(a, pm); - pa = pb = (char *) a + es; - pc = pd = (char *) a + (n - 1) * es; + pa = pb = (char *)a + es; + pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a, arg)) <= 0) @@ -189,8 +253,8 @@ loop:SWAPINIT(a, es); pb += es; pc -= es; } - pn = (char *) a + n * es; - d1 = Min(pa - (char *) a, pb - pa); + pn = (char *)a + n * es; + d1 = Min(pa - (char *)a, pb - pa); vecswap(a, pb - d1, d1); d1 = Min(pd - pc, pn - pd - es); vecswap(pb, pn - d1, d1); @@ -200,13 +264,14 @@ loop:SWAPINIT(a, es); { /* Recurse on left partition, then iterate on right partition */ if (d1 > es) - qsort_arg(a, d1 / es, es, cmp, arg); + pg_qsort_arg_recursive(a, d1 / es, depth - 1, swaptype, es, cmp, arg); if (d2 > es) { /* Iterate rather than recurse to save stack space */ - /* qsort_arg(pn - d2, d2 / es, es, cmp, arg); */ + /* pg_qsort_arg_recursive(pn - d2, d2 / es, depth - 1, es, cmp, arg); */ a = pn - d2; n = d2 / es; + depth--; goto loop; } } @@ -214,13 +279,23 @@ loop:SWAPINIT(a, es); { /* Recurse on right partition, then iterate on left partition */ if (d2 > es) - qsort_arg(pn - d2, d2 / es, es, cmp, arg); + pg_qsort_arg_recursive(pn - d2, d2 / es, depth - 1, swaptype, es, cmp, arg); if (d1 > es) { /* Iterate rather than recurse to save stack space */ - /* qsort_arg(a, d1 / es, es, cmp, arg); */ + /* pg_qsort_arg_recursive(a, d1 / es, depth - 1, es, cmp, arg); */ n = d1 / es; + depth--; goto loop; } } } + + +void +qsort_arg(void *a, const size_t size, const size_t es, qsort_arg_comparator cmp, void* arg) { + int swaptype; + SWAPINIT(a, es); + pg_qsort_arg_recursive(a, size, 2 * log(size), swaptype, es, cmp, arg); +}; +