From: Ronan Dunklau <ronan.dunklau@aiven.io>
To: James Coleman <jtc331@gmail.com>
Cc: PostgreSQL Developers <pgsql-hackers@lists.postgresql.org>
Cc: Ranier Vilela <ranier.vf@gmail.com>
Cc: David Rowley <dgrowleyml@gmail.com>
Cc: Dilip Kumar <dilipbalaut@gmail.com>
Subject: Re: [PATCH] Use optimized single-datum tuplesort in ExecSort
Date: Wed, 07 Jul 2021 11:32:03 +0200
Message-ID: <3060002.hb0XKQ11pn@aivenronan> (raw)
In-Reply-To: <CAAaqYe9qVpWqJp3j9rOtbupJQM_m_PO1RqfrC-W5=+Kz7cqXqg@mail.gmail.com>
References: <3177670.itZtoPt7T5@aivenronan>
<4385594.q5DUe7zQD7@aivenronan>
<CAAaqYe9qVpWqJp3j9rOtbupJQM_m_PO1RqfrC-W5=+Kz7cqXqg@mail.gmail.com>
Le mardi 6 juillet 2021, 17:37:53 CEST James Coleman a écrit :
> Yes and no. When incremental sort has to do a full sort there will
> always be at least 2 attributes. But in prefix sort mode (see
> prefixsort_state) only non-presorted columns are sorted (i.e., if
> given a,b already sorted by a, then only b is sorted). So the
> prefixsort_state could use this optimization.
The optimization is not when we actually sort on a single key, but when we get
a single attribute in / out of the tuplesort. Since sorting always add
resjunk entries for the keys being sorted on, I don't think we can ever end up
in a situation where the optimization would kick in, since the entries for the
already-performed-sort keys will need to be present in the output.
Maybe if instead of adding resjunk entries to the whole query's targetlist,
sort and incrementalsort nodes were able to do a projection from the input
(needed tle + resjunk sorting tle) to a tuple containing only the needed tle
on output before actually sorting it, it would be possible, but that would be
quite a big design change.
In the meantime I fixed some formatting issues, please find attached a new
patch.
--
Ronan Dunklau
Attachments:v4-0001-Allow-Sort-nodes-to-use-the-fast-single-datum-tuples.patch (text/x-patch, 4.9K) downloaddiff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.cindex b99027e0d7..9d8b0a77da 100644--- a/src/backend/executor/nodeSort.c+++ b/src/backend/executor/nodeSort.c@@ -29,6 +29,10 @@
* which saves the results in a temporary file or memory. After the
* initial call, returns a tuple from the file with each call.
*
+ * The tuplesort can either occur on the whole tuple (this is the nominal+ * case) or, when the input / output tuple consists of only one attribute,+ * we switch to the tuplesort_*_datum API, optimized for that specific case.+ *
* Conditions:
* -- none.
*
@@ -86,32 +90,61 @@ ExecSort(PlanState *pstate)
outerNode = outerPlanState(node);
tupDesc = ExecGetResultType(outerNode);
- tuplesortstate = tuplesort_begin_heap(tupDesc,- plannode->numCols,- plannode->sortColIdx,- plannode->sortOperators,- plannode->collations,- plannode->nullsFirst,- work_mem,- NULL,- node->randomAccess);+ /*+ * Switch to the tuplesort_*_datum interface when we have only one+ * key, as it is much more efficient especially when the type is+ * pass-by-value.+ */+ if (tupDesc->natts == 1)+ {+ node->is_single_val = true;+ tuplesortstate = tuplesort_begin_datum(TupleDescAttr(tupDesc, 0)->atttypid,+ plannode->sortOperators[0],+ plannode->collations[0],+ plannode->nullsFirst[0],+ work_mem,+ NULL,+ node->randomAccess);+ }+ else+ tuplesortstate = tuplesort_begin_heap(tupDesc,+ plannode->numCols,+ plannode->sortColIdx,+ plannode->sortOperators,+ plannode->collations,+ plannode->nullsFirst,+ work_mem,+ NULL,+ node->randomAccess);
if (node->bounded)
tuplesort_set_bound(tuplesortstate, node->bound);
node->tuplesortstate = (void *) tuplesortstate;
/*
- * Scan the subplan and feed all the tuples to tuplesort.+ * Scan the subplan and feed all the tuples to tuplesort, using either+ * the _putdatum or _puttupleslot API as appropriate.
*/
-- for (;;)- {- slot = ExecProcNode(outerNode);-- if (TupIsNull(slot))- break;-- tuplesort_puttupleslot(tuplesortstate, slot);- }+ if (node->is_single_val)+ for (;;)+ {+ slot = ExecProcNode(outerNode);++ if (TupIsNull(slot))+ break;+ slot_getsomeattrs(slot, 1);+ tuplesort_putdatum(tuplesortstate,+ slot->tts_values[0],+ slot->tts_isnull[0]);+ }+ else+ for (;;)+ {+ slot = ExecProcNode(outerNode);++ if (TupIsNull(slot))+ break;+ tuplesort_puttupleslot(tuplesortstate, slot);+ }
/*
* Complete the sort.
@@ -150,9 +183,18 @@ ExecSort(PlanState *pstate)
* next fetch from the tuplesort.
*/
slot = node->ss.ps.ps_ResultTupleSlot;
- (void) tuplesort_gettupleslot(tuplesortstate,- ScanDirectionIsForward(dir),- false, slot, NULL);+ if (node->is_single_val)+ {+ ExecClearTuple(slot);+ if (tuplesort_getdatum(tuplesortstate, ScanDirectionIsForward(dir),+ &(slot->tts_values[0]), &(slot->tts_isnull[0]), NULL))+ ExecStoreVirtualTuple(slot);+ }+ else+ (void) tuplesort_gettupleslot(tuplesortstate,+ ScanDirectionIsForward(dir),+ false, slot, NULL);+
return slot;
}
@@ -191,6 +233,7 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
sortstate->bounded = false;
sortstate->sort_Done = false;
sortstate->tuplesortstate = NULL;
+ sortstate->is_single_val = false;
/*
* Miscellaneous initialization
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.cindex 22972071ff..f785259da7 100644--- a/src/backend/utils/sort/tuplesort.c+++ b/src/backend/utils/sort/tuplesort.c@@ -4773,6 +4773,14 @@ leader_takeover_tapes(Tuplesortstate *state)
static void
free_sort_tuple(Tuplesortstate *state, SortTuple *stup)
{
- FREEMEM(state, GetMemoryChunkSpace(stup->tuple));- pfree(stup->tuple);+ /*+ * If the SortTuple is actually only a single Datum, which was not copied+ * as it is a byval type, do not try to free it nor account for it in+ * memory used.+ */+ if (stup->tuple)+ {+ FREEMEM(state, GetMemoryChunkSpace(stup->tuple));+ pfree(stup->tuple);+ }
}
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.hindex 0ec5509e7e..643f416c54 100644--- a/src/include/nodes/execnodes.h+++ b/src/include/nodes/execnodes.h@@ -2151,6 +2151,7 @@ typedef struct SortState
int64 bound_Done; /* value of bound we did the sort with */
void *tuplesortstate; /* private state of tuplesort.c */
bool am_worker; /* are we a worker? */
+ bool is_single_val; /* are we using the single value optimization ? */
SharedSortInfo *shared_info; /* one entry per worker */
} SortState;
--
2.32.0
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: ronan.dunklau@aiven.io, jtc331@gmail.com, pgsql-hackers@lists.postgresql.org, ranier.vf@gmail.com, dgrowleyml@gmail.com, dilipbalaut@gmail.com
Subject: Re: [PATCH] Use optimized single-datum tuplesort in ExecSort
In-Reply-To: <3060002.hb0XKQ11pn@aivenronan>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox