1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00

Specialize tuplesort routines for different kinds of abbreviated keys

Previously, the specialized tuplesort routine inlined handling for
reverse-sort and NULLs-ordering but called the datum comparator via a
pointer in the SortSupport struct parameter. Testing has showed that we
can get a useful performance gain by specializing datum comparison for
the different representations of abbreviated keys -- signed and unsigned
64-bit integers and signed 32-bit integers. Almost all abbreviatable data
types will benefit -- the only exception for now is numeric, since the
datum comparison is more complex. The performance gain depends on data
type and input distribution, but often falls in the range of 10-20% faster.

Thomas Munro

Reviewed by Peter Geoghegan, review and performance testing by me

Discussion:
https://www.postgresql.org/message-id/CA%2BhUKGKKYttZZk-JMRQSVak%3DCXSJ5fiwtirFf%3Dn%3DPAbumvn1Ww%40mail.gmail.com
This commit is contained in:
John Naylor
2022-04-02 15:22:25 +07:00
parent db086de5ab
commit 6974924347
10 changed files with 308 additions and 132 deletions

View File

@@ -669,14 +669,101 @@ static void free_sort_tuple(Tuplesortstate *state, SortTuple *stup);
static void tuplesort_free(Tuplesortstate *state);
static void tuplesort_updatemax(Tuplesortstate *state);
/*
* Specialized comparators that we can inline into specialized sorts. The goal
* is to try to sort two tuples without having to follow the pointers to the
* comparator or the tuple.
*
* XXX: For now, these fall back to comparator functions that will compare the
* leading datum a second time.
*
* XXX: For now, there is no specialization for cases where datum1 is
* authoritative and we don't even need to fall back to a callback at all (that
* would be true for types like int4/int8/timestamp/date, but not true for
* abbreviations of text or multi-key sorts. There could be! Is it worth it?
*/
/* Used if first key's comparator is ssup_datum_unsigned_compare */
static pg_attribute_always_inline int
qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
{
int compare;
compare = ApplyUnsignedSortComparator(a->datum1, a->isnull1,
b->datum1, b->isnull1,
&state->sortKeys[0]);
if (compare != 0)
return compare;
return state->comparetup(a, b, state);
}
/* Used if first key's comparator is ssup_datum_signed_compare */
static pg_attribute_always_inline int
qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
{
int compare;
compare = ApplySignedSortComparator(a->datum1, a->isnull1,
b->datum1, b->isnull1,
&state->sortKeys[0]);
if (compare != 0)
return compare;
return state->comparetup(a, b, state);
}
/* Used if first key's comparator is ssup_datum_int32_compare */
static pg_attribute_always_inline int
qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state)
{
int compare;
compare = ApplyInt32SortComparator(a->datum1, a->isnull1,
b->datum1, b->isnull1,
&state->sortKeys[0]);
if (compare != 0)
return compare;
return state->comparetup(a, b, state);
}
/*
* Special versions of qsort just for SortTuple objects. qsort_tuple() sorts
* any variant of SortTuples, using the appropriate comparetup function.
* qsort_ssup() is specialized for the case where the comparetup function
* reduces to ApplySortComparator(), that is single-key MinimalTuple sorts
* and Datum sorts.
* and Datum sorts. qsort_tuple_{unsigned,signed,int32} are specialized for
* common comparison functions on pass-by-value leading datums.
*/
#define ST_SORT qsort_tuple_unsigned
#define ST_ELEMENT_TYPE SortTuple
#define ST_COMPARE(a, b, state) qsort_tuple_unsigned_compare(a, b, state)
#define ST_COMPARE_ARG_TYPE Tuplesortstate
#define ST_CHECK_FOR_INTERRUPTS
#define ST_SCOPE static
#define ST_DEFINE
#include "lib/sort_template.h"
#define ST_SORT qsort_tuple_signed
#define ST_ELEMENT_TYPE SortTuple
#define ST_COMPARE(a, b, state) qsort_tuple_signed_compare(a, b, state)
#define ST_COMPARE_ARG_TYPE Tuplesortstate
#define ST_CHECK_FOR_INTERRUPTS
#define ST_SCOPE static
#define ST_DEFINE
#include "lib/sort_template.h"
#define ST_SORT qsort_tuple_int32
#define ST_ELEMENT_TYPE SortTuple
#define ST_COMPARE(a, b, state) qsort_tuple_int32_compare(a, b, state)
#define ST_COMPARE_ARG_TYPE Tuplesortstate
#define ST_CHECK_FOR_INTERRUPTS
#define ST_SCOPE static
#define ST_DEFINE
#include "lib/sort_template.h"
#define ST_SORT qsort_tuple
#define ST_ELEMENT_TYPE SortTuple
#define ST_COMPARE_RUNTIME_POINTER
@@ -3506,15 +3593,40 @@ tuplesort_sort_memtuples(Tuplesortstate *state)
if (state->memtupcount > 1)
{
/* Do we have a specialization for the leading column's comparator? */
if (state->sortKeys &&
state->sortKeys[0].comparator == ssup_datum_unsigned_cmp)
{
elog(DEBUG1, "qsort_tuple_unsigned");
qsort_tuple_unsigned(state->memtuples, state->memtupcount, state);
}
else if (state->sortKeys &&
state->sortKeys[0].comparator == ssup_datum_signed_cmp)
{
elog(DEBUG1, "qsort_tuple_signed");
qsort_tuple_signed(state->memtuples, state->memtupcount, state);
}
else if (state->sortKeys &&
state->sortKeys[0].comparator == ssup_datum_int32_cmp)
{
elog(DEBUG1, "qsort_tuple_int32");
qsort_tuple_int32(state->memtuples, state->memtupcount, state);
}
/* Can we use the single-key sort function? */
if (state->onlyKey != NULL)
else if (state->onlyKey != NULL)
{
elog(DEBUG1, "qsort_ssup");
qsort_ssup(state->memtuples, state->memtupcount,
state->onlyKey);
}
else
{
elog(DEBUG1, "qsort_tuple");
qsort_tuple(state->memtuples,
state->memtupcount,
state->comparetup,
state);
}
}
}
@@ -4700,3 +4812,47 @@ free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
stup->tuple = NULL;
}
}
int
ssup_datum_unsigned_cmp(Datum x, Datum y, SortSupport ssup)
{
if (x < y)
return -1;
else if (x > y)
return 1;
else
return 0;
}
int
ssup_datum_signed_cmp(Datum x, Datum y, SortSupport ssup)
{
#if SIZEOF_DATUM == 8
int64 xx = (int64) x;
int64 yy = (int64) y;
#else
int32 xx = (int32) x;
int32 yy = (int32) y;
#endif
if (xx < yy)
return -1;
else if (xx > yy)
return 1;
else
return 0;
}
int
ssup_datum_int32_cmp(Datum x, Datum y, SortSupport ssup)
{
int32 xx = (int32) x;
int32 yy = (int32) y;
if (xx < yy)
return -1;
else if (xx > yy)
return 1;
else
return 0;
}