v8_poc_datum_tuple_sort.patch

application/octet-stream

Filename: v8_poc_datum_tuple_sort.patch
Type: application/octet-stream
Part: 0
Message: Re: [PATCH] Use optimized single-datum tuplesort in ExecSort

Patch

Format: unified
Series: patch v8
File+
src/backend/executor/nodeSort.c 146 7
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index b99027e0d7..f65755d019 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -29,6 +29,16 @@
  *		which saves the results in a temporary file or memory. After the
  *		initial call, returns a tuple from the file with each call.
  *
+ *		There are two distinct ways that we perform this sort:
+ *
+ *		1) When the result is a single column we perform a Datum sort.
+ *
+ *		2) When the result contains multiple columns we perform a tuple sort.
+ *
+ *		We could do this by always performing a tuple sort, however sorting
+ *		Datums only can be significantly faster than sorting tuples,
+ *		especially when the Datums are of a byval type.
+ *
  *		Conditions:
  *		  -- none.
  *
@@ -37,7 +47,7 @@
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-ExecSort(PlanState *pstate)
+ExecSortTuple(PlanState *pstate)
 {
 	SortState  *node = castNode(SortState, pstate);
 	EState	   *estate;
@@ -50,7 +60,7 @@ ExecSort(PlanState *pstate)
 	/*
 	 * get state info from node
 	 */
-	SO1_printf("ExecSort: %s\n",
+	SO1_printf("ExecSortTuple: %s\n",
 			   "entering routine");
 
 	estate = node->ss.ps.state;
@@ -68,7 +78,7 @@ ExecSort(PlanState *pstate)
 		PlanState  *outerNode;
 		TupleDesc	tupDesc;
 
-		SO1_printf("ExecSort: %s\n",
+		SO1_printf("ExecSortTuple: %s\n",
 				   "sorting subplan");
 
 		/*
@@ -80,7 +90,7 @@ ExecSort(PlanState *pstate)
 		/*
 		 * Initialize tuplesort module.
 		 */
-		SO1_printf("ExecSort: %s\n",
+		SO1_printf("ExecSortTuple: %s\n",
 				   "calling tuplesort_begin");
 
 		outerNode = outerPlanState(node);
@@ -138,10 +148,10 @@ ExecSort(PlanState *pstate)
 			si = &node->shared_info->sinstrument[ParallelWorkerNumber];
 			tuplesort_get_stats(tuplesortstate, si);
 		}
-		SO1_printf("ExecSort: %s\n", "sorting done");
+		SO1_printf("ExecSortTuple: %s\n", "sorting done");
 	}
 
-	SO1_printf("ExecSort: %s\n",
+	SO1_printf("ExecSortTuple: %s\n",
 			   "retrieving tuple from tuplesort");
 
 	/*
@@ -156,6 +166,127 @@ ExecSort(PlanState *pstate)
 	return slot;
 }
 
+static TupleTableSlot *
+ExecSortDatum(PlanState *pstate)
+{
+	SortState  *node = castNode(SortState, pstate);
+	EState	   *estate;
+	ScanDirection dir;
+	Tuplesortstate *tuplesortstate;
+	TupleTableSlot *slot;
+
+	CHECK_FOR_INTERRUPTS();
+
+	/*
+	 * get state info from node
+	 */
+	SO1_printf("ExecSort: %s\n",
+			   "entering routine");
+
+	estate = node->ss.ps.state;
+	dir = estate->es_direction;
+	tuplesortstate = (Tuplesortstate *) node->tuplesortstate;
+
+	/*
+	 * If first time through, read all tuples from outer plan and pass them to
+	 * tuplesort.c. Subsequent calls just fetch tuples from tuplesort.
+	 */
+
+	if (!node->sort_Done)
+	{
+		Sort	   *plannode = (Sort *) node->ss.ps.plan;
+		PlanState  *outerNode;
+		TupleDesc	tupDesc;
+
+		SO1_printf("ExecSortDatum: %s\n",
+				   "sorting subplan");
+
+		/*
+		 * Want to scan subplan in the forward direction while creating the
+		 * sorted data.
+		 */
+		estate->es_direction = ForwardScanDirection;
+
+		/*
+		 * Initialize tuplesort module.
+		 */
+		SO1_printf("ExecSortDatum: %s\n",
+				   "calling tuplesort_begin");
+
+		outerNode = outerPlanState(node);
+		tupDesc = ExecGetResultType(outerNode);
+
+		tuplesortstate = tuplesort_begin_datum(TupleDescAttr(tupDesc, 0)->atttypid,
+											   plannode->sortOperators[0],
+											   plannode->collations[0],
+											   plannode->nullsFirst[0],
+											   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.
+		 */
+
+		for (;;)
+		{
+			slot = ExecProcNode(outerNode);
+
+			if (TupIsNull(slot))
+				break;
+			slot_getsomeattrs(slot, 1);
+			tuplesort_putdatum(tuplesortstate,
+							   slot->tts_values[0],
+							   slot->tts_isnull[0]);
+		}
+
+		/*
+		 * Complete the sort.
+		 */
+		tuplesort_performsort(tuplesortstate);
+
+		/*
+		 * restore to user specified direction
+		 */
+		estate->es_direction = dir;
+
+		/*
+		 * finally set the sorted flag to true
+		 */
+		node->sort_Done = true;
+		node->bounded_Done = node->bounded;
+		node->bound_Done = node->bound;
+		if (node->shared_info && node->am_worker)
+		{
+			TuplesortInstrumentation *si;
+
+			Assert(IsParallelWorker());
+			Assert(ParallelWorkerNumber <= node->shared_info->num_workers);
+			si = &node->shared_info->sinstrument[ParallelWorkerNumber];
+			tuplesort_get_stats(tuplesortstate, si);
+		}
+		SO1_printf("ExecSortDatum: %s\n", "sorting done");
+	}
+
+	SO1_printf("ExecSortDatum: %s\n",
+			   "retrieving datum from tuplesort");
+
+	/*
+	 * Get the first or next datum from tuplesort. We clear the slot before
+	 * doing this.  This means when we run out of datums to get that the
+	 * function returns false and the slot is left empty.
+	 */
+	slot = node->ss.ps.ps_ResultTupleSlot;
+	ExecClearTuple(slot);
+	if (tuplesort_getdatum(tuplesortstate, ScanDirectionIsForward(dir),
+						   &(slot->tts_values[0]), &(slot->tts_isnull[0]), NULL))
+		ExecStoreVirtualTuple(slot);
+	return slot;
+}
+
 /* ----------------------------------------------------------------
  *		ExecInitSort
  *
@@ -177,7 +308,6 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	sortstate = makeNode(SortState);
 	sortstate->ss.ps.plan = (Plan *) node;
 	sortstate->ss.ps.state = estate;
-	sortstate->ss.ps.ExecProcNode = ExecSort;
 
 	/*
 	 * We must have random access to the sort output to do backward scan or
@@ -221,6 +351,15 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple);
 	sortstate->ss.ps.ps_ProjInfo = NULL;
 
+	/*
+	 * We perform a Datum sort when we're sorting just a single column,
+	 * otherwise we perform a tuple sort.
+	 */
+	if (ExecGetResultType(outerPlanState(sortstate))->natts == 1)
+		sortstate->ss.ps.ExecProcNode = ExecSortDatum;
+	else
+		sortstate->ss.ps.ExecProcNode = ExecSortTuple;
+
 	SO1_printf("ExecInitSort: %s\n",
 			   "sort node initialized");