--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# gen_qsort_tuple.pl
+#
+# This script generates specialized versions of the quicksort algorithm for
+# tuple sorting. The quicksort code is derived from the NetBSD code. The
+# code generated by this script runs significantly faster than vanilla qsort
+# when used to sort tuples. This speedup comes from a number of places.
+# The major effects are (1) inlining simple tuple comparators is much faster
+# than jumping through a function pointer and (2) swap and vecswap operations
+# specialized to the particular data type of interest (in this case, SortTuple)
+# are faster than the generic routines.
+#
+# Modifications from vanilla NetBSD source:
+# Add do ... while() macro fix
+# Remove __inline, _DIAGASSERTs, __P
+# Remove ill-considered "swap_cnt" switch to insertion sort,
+# in favor of a simple check for presorted input.
+# Instead of sorting arbitrary objects, we're always sorting SortTuples
+# Add CHECK_FOR_INTERRUPTS()
+#
+# CAUTION: if you change this file, see also qsort.c and qsort_arg.c
+#
+
+use strict;
+
+my $SUFFIX;
+my $EXTRAARGS;
+my $EXTRAPARAMS;
+my $CMPPARAMS;
+
+emit_qsort_boilerplate();
+
+$SUFFIX = 'tuple';
+$EXTRAARGS = ', SortTupleComparator cmp_tuple, Tuplesortstate *state';
+$EXTRAPARAMS = ', cmp_tuple, state';
+$CMPPARAMS = ', state';
+emit_qsort_implementation();
+
+$SUFFIX = 'ssup';
+$EXTRAARGS = ', SortSupport ssup';
+$EXTRAPARAMS = ', ssup';
+$CMPPARAMS = ', ssup';
+print <<'EOM';
+#define cmp_ssup(a, b, ssup) \
+ ApplySortComparator((a)->datum1, (a)->isnull1, \
+ (b)->datum1, (b)->isnull1, ssup)
+EOM
+emit_qsort_implementation();
+
+sub emit_qsort_boilerplate
+{
+ print <<'EOM';
+/*
+ * autogenerated by src/backend/utils/sort/gen_qsort_tuple.pl, do not edit
+ * This file is included by tuplesort.c, rather than compiled separately.
+ */
+
+/* $NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Qsort routine based on J. L. Bentley and M. D. McIlroy,
+ * "Engineering a sort function",
+ * Software--Practice and Experience 23 (1993) 1249-1265.
+ * We have modified their original by adding a check for already-sorted input,
+ * which seems to be a win per discussions on pgsql-hackers around 2006-03-21.
+ */
+
+static void
+swapfunc(SortTuple *a, SortTuple *b, size_t n)
+{
+ do
+ {
+ SortTuple t = *a;
+ *a++ = *b;
+ *b++ = t;
+ } while (--n > 0);
+}
+
+#define swap(a, b) \
+ do { \
+ SortTuple t = *(a); \
+ *(a) = *(b); \
+ *(b) = t; \
+ } while (0);
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc((a), (b), (size_t)(n))
+EOM
+}
+
+sub emit_qsort_implementation
+{
+ print <<EOM;
+static SortTuple *
+med3_$SUFFIX(SortTuple *a, SortTuple *b, SortTuple *c$EXTRAARGS)
+{
+ return cmp_$SUFFIX(a, b$CMPPARAMS) < 0 ?
+ (cmp_$SUFFIX(b, c$CMPPARAMS) < 0 ? b :
+ (cmp_$SUFFIX(a, c$CMPPARAMS) < 0 ? c : a))
+ : (cmp_$SUFFIX(b, c$CMPPARAMS) > 0 ? b :
+ (cmp_$SUFFIX(a, c$CMPPARAMS) < 0 ? a : c));
+}
+
+static void
+qsort_$SUFFIX(SortTuple *a, size_t n$EXTRAARGS)
+{
+ SortTuple *pa,
+ *pb,
+ *pc,
+ *pd,
+ *pl,
+ *pm,
+ *pn;
+ int d,
+ r,
+ presorted;
+
+loop:
+ CHECK_FOR_INTERRUPTS();
+ if (n < 7)
+ {
+ for (pm = a + 1; pm < a + n; pm++)
+ for (pl = pm; pl > a && cmp_$SUFFIX(pl - 1, pl$CMPPARAMS) > 0; pl--)
+ swap(pl, pl - 1);
+ return;
+ }
+ presorted = 1;
+ for (pm = a + 1; pm < a + n; pm++)
+ {
+ CHECK_FOR_INTERRUPTS();
+ if (cmp_$SUFFIX(pm - 1, pm$CMPPARAMS) > 0)
+ {
+ presorted = 0;
+ break;
+ }
+ }
+ if (presorted)
+ return;
+ pm = a + (n / 2);
+ if (n > 7)
+ {
+ pl = a;
+ pn = a + (n - 1);
+ if (n > 40)
+ {
+ d = (n / 8);
+ pl = med3_$SUFFIX(pl, pl + d, pl + 2 * d$EXTRAPARAMS);
+ pm = med3_$SUFFIX(pm - d, pm, pm + d$EXTRAPARAMS);
+ pn = med3_$SUFFIX(pn - 2 * d, pn - d, pn$EXTRAPARAMS);
+ }
+ pm = med3_$SUFFIX(pl, pm, pn$EXTRAPARAMS);
+ }
+ swap(a, pm);
+ pa = pb = a + 1;
+ pc = pd = a + (n - 1);
+ for (;;)
+ {
+ while (pb <= pc && (r = cmp_$SUFFIX(pb, a$CMPPARAMS)) <= 0)
+ {
+ CHECK_FOR_INTERRUPTS();
+ if (r == 0)
+ {
+ swap(pa, pb);
+ pa++;
+ }
+ pb++;
+ }
+ while (pb <= pc && (r = cmp_$SUFFIX(pc, a$CMPPARAMS)) >= 0)
+ {
+ CHECK_FOR_INTERRUPTS();
+ if (r == 0)
+ {
+ swap(pc, pd);
+ pd--;
+ }
+ pc--;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ pb++;
+ pc--;
+ }
+ pn = a + n;
+ r = Min(pa - a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = Min(pd - pc, pn - pd - 1);
+ vecswap(pb, pn - r, r);
+ if ((r = pb - pa) > 1)
+ qsort_$SUFFIX(a, r$EXTRAPARAMS);
+ if ((r = pd - pc) > 1)
+ {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r;
+ goto loop;
+ }
+/* qsort_$SUFFIX(pn - r, r$EXTRAPARAMS);*/
+}
+
+EOM
+}
#define TAPE_BUFFER_OVERHEAD (BLCKSZ * 3)
#define MERGE_BUFFER_SIZE (BLCKSZ * 32)
+typedef int (*SortTupleComparator) (const SortTuple *a, const SortTuple *b,
+ Tuplesortstate *state);
+
/*
* Private state of a Tuplesort operation.
*/
* <0, 0, >0 according as a<b, a=b, a>b. The API must match
* qsort_arg_comparator.
*/
- int (*comparetup) (const SortTuple *a, const SortTuple *b,
- Tuplesortstate *state);
+ SortTupleComparator comparetup;
/*
* Function to copy a supplied input tuple into palloc'd space and set up
/* These are specific to the index_hash subcase: */
uint32 hash_mask; /* mask for sortable part of hash code */
+ /* This is initialized when, and only when, there's just one key. */
+ SortSupport onlyKey;
+
/*
* These variables are specific to the Datum case; they are set by
* tuplesort_begin_datum and used only by the DatumTuple routines.
*/
Oid datumType;
- SortSupport datumKey;
/* we need typelen and byval in order to know how to copy the Datums. */
int datumTypeLen;
bool datumTypeByVal;
static void reversedirection_datum(Tuplesortstate *state);
static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
+/*
+ * Special version of qsort, just for SortTuple objects.
+ */
+#include "qsort_tuple.c"
+
/*
* tuplesort_begin_xxx
PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
}
+ if (nkeys == 1)
+ state->onlyKey = state->sortKeys;
+
MemoryContextSwitchTo(oldcontext);
return state;
state->datumType = datumType;
/* Prepare SortSupport data */
- state->datumKey = (SortSupport) palloc0(sizeof(SortSupportData));
+ state->onlyKey = (SortSupport) palloc0(sizeof(SortSupportData));
- state->datumKey->ssup_cxt = CurrentMemoryContext;
- state->datumKey->ssup_collation = sortCollation;
- state->datumKey->ssup_nulls_first = nullsFirstFlag;
+ state->onlyKey->ssup_cxt = CurrentMemoryContext;
+ state->onlyKey->ssup_collation = sortCollation;
+ state->onlyKey->ssup_nulls_first = nullsFirstFlag;
- PrepareSortSupportFromOrderingOp(sortOperator, state->datumKey);
+ PrepareSortSupportFromOrderingOp(sortOperator, state->onlyKey);
/* lookup necessary attributes of the datum type */
get_typlenbyval(datumType, &typlen, &typbyval);
* amount of memory. Just qsort 'em and we're done.
*/
if (state->memtupcount > 1)
- qsort_arg((void *) state->memtuples,
- state->memtupcount,
- sizeof(SortTuple),
- (qsort_arg_comparator) state->comparetup,
- (void *) state);
+ {
+ if (state->onlyKey != NULL)
+ qsort_ssup(state->memtuples, state->memtupcount,
+ state->onlyKey);
+ else
+ qsort_tuple(state->memtuples,
+ state->memtupcount,
+ state->comparetup,
+ state);
+ }
state->current = 0;
state->eof_reached = false;
state->markpos_offset = 0;
int nkey;
int32 compare;
- /* Allow interrupting long sorts */
- CHECK_FOR_INTERRUPTS();
-
/* Compare the leading sort key */
compare = ApplySortComparator(a->datum1, a->isnull1,
b->datum1, b->isnull1,
int nkey;
int32 compare;
- /* Allow interrupting long sorts */
- CHECK_FOR_INTERRUPTS();
-
/* Compare the leading sort key, if it's simple */
if (state->indexInfo->ii_KeyAttrNumbers[0] != 0)
{
int nkey;
int32 compare;
- /* Allow interrupting long sorts */
- CHECK_FOR_INTERRUPTS();
-
/* Compare the leading sort key */
compare = inlineApplySortFunction(&scanKey->sk_func, scanKey->sk_flags,
scanKey->sk_collation,
IndexTuple tuple1;
IndexTuple tuple2;
- /* Allow interrupting long sorts */
- CHECK_FOR_INTERRUPTS();
-
/*
* Fetch hash keys and mask off bits we don't want to sort by. We know
* that the first column of the index tuple is the hash key.
static int
comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
- /* Allow interrupting long sorts */
- CHECK_FOR_INTERRUPTS();
-
- return ApplySortComparator(a->datum1, a->isnull1,
- b->datum1, b->isnull1,
- state->datumKey);
+ /* Not currently needed */
+ elog(ERROR, "comparetup_datum() should not be called");
+ return 0;
}
static void
static void
reversedirection_datum(Tuplesortstate *state)
{
- state->datumKey->ssup_reverse = !state->datumKey->ssup_reverse;
- state->datumKey->ssup_nulls_first = !state->datumKey->ssup_nulls_first;
+ state->onlyKey->ssup_reverse = !state->onlyKey->ssup_reverse;
+ state->onlyKey->ssup_nulls_first = !state->onlyKey->ssup_nulls_first;
}
/*