From e4ad1dba29aae41f741020288008f9b0866a381d Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Mon, 30 May 2022 14:19:19 +0700
Subject: [PATCH v1 4/5] Optimize sorts on likely reversed input

If we have nine pivot candidates, and they are in reversed order,
preemptively reverse the whole sequence and treat it as likely sorted.
---
 src/include/lib/sort_template.h         | 29 ++++++++++++++++++++++---
 src/test/regress/expected/tuplesort.out |  2 +-
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index 492ec2823b..ed21af297c 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -59,6 +59,7 @@
  *	  - Remove __inline, _DIAGASSERTs, __P
  *	  - Take care to recurse on the smaller partition, to bound stack usage
  *	  - Convert into a header that can generate specialized functions
+ *	  - Add optimization for likely reversed input
  *
  * IDENTIFICATION
  *		src/include/lib/sort_template.h
@@ -246,7 +247,7 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n
 /*
  * Find the median of three values, and count the number of out-of-order
  * elements. We don't care to keep an accurate count, only that we count
- * report 0 only if the elements are presorted.
+ * report 0 if the elements are presorted and 3 if they are in reverse order.
  *
  * Currently, performance seems to be best
  * if the comparator is inlined here, but the med3 function is not inlined
@@ -265,10 +266,10 @@ ST_MED3(ST_ELEMENT_TYPE * a,
 		(*num_reversed)++;
 		if (DO_COMPARE(c, a) < 0)
 		{
-			/* (*num_reversed)++; WIP */
+			(*num_reversed)++;
 			if (DO_COMPARE(c, b) < 0)
 			{
-				/* (*num_reversed)++; WIP */
+				(*num_reversed)++;
 				return b;
 			}
 			else
@@ -419,6 +420,28 @@ loop:
 		pm = DO_MED3(pl, pm, pn, &num_reversed);
 	}
 
+	/* if the pivot candidates were in reverse order, reverse the whole sequence */
+	if (num_reversed == 4 * 3) // 4 calls of MED3
+	{
+		ST_POINTER_TYPE * i = a;
+		ST_POINTER_TYPE * j = a + (n - 1) * ST_POINTER_STEP;
+
+		Assert(n > ST_THRESHOLD_MED9);
+		while (i < j)
+		{
+			DO_SWAP(i, j);
+			DO_CHECK_FOR_INTERRUPTS();
+			i += ST_POINTER_STEP;
+			j -= ST_POINTER_STEP;
+		}
+
+		/* reverse pivot position */
+		pm = (a + (n - 1) * ST_POINTER_STEP) - (pm - a);
+
+		/* hint that the sequence is now likely sorted */
+		num_reversed = 0;
+	}
+
 	// If the sequence was decently balanced and already partitioned,
 	// and the pivot candidates were presorted, try to use insertion sort.
 	if (already_partitioned && balanced && num_reversed == 0)
diff --git a/src/test/regress/expected/tuplesort.out b/src/test/regress/expected/tuplesort.out
index a1a5889756..4bdd466a09 100644
--- a/src/test/regress/expected/tuplesort.out
+++ b/src/test/regress/expected/tuplesort.out
@@ -260,8 +260,8 @@ FROM abbrev_abort_uuids
 ORDER BY ctid LIMIT 5;
   id   |           abort_increasing           |           abort_decreasing           |          noabort_increasing          |          noabort_decreasing          
 -------+--------------------------------------+--------------------------------------+--------------------------------------+--------------------------------------
- 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
  20001 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
+ 20010 | 00000000-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000 | 00009991-0000-0000-0000-000000020000 | 00000000-0000-0000-0000-000000000000
  20000 | 00000000-0000-0000-0000-000000019999 | 00000000-0000-0000-0000-000000000001 | 00009990-0000-0000-0000-000000019999 | 00000001-0000-0000-0000-000000000001
  19999 | 00000000-0000-0000-0000-000000019998 | 00000000-0000-0000-0000-000000000002 | 00009989-0000-0000-0000-000000019998 | 00000002-0000-0000-0000-000000000002
  20009 | 00000000-0000-0000-0000-000000019997 | 00000000-0000-0000-0000-000000000003 | 00009988-0000-0000-0000-000000019997 | 00000003-0000-0000-0000-000000000003
-- 
2.36.1

