Sort functions with specialized comparators

Started by Andrey M. Borodinover 1 year ago38 messages
#1Andrey M. Borodin
x4mmm@yandex-team.ru
1 attachment(s)

Hi!

In a thread about sorting comparators[0]/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de Andres noted that we have infrastructure to help compiler optimize sorting. PFA attached PoC implementation. I've checked that it indeed works on the benchmark from that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber, Oid etc. But this sorting routines never show up in perf top or something like that.

Seems like in most cases we do not spend much time in sorting. But specialization does not cost us much too, only some CPU cycles of a compiler. I think we can further improve speedup by converting inline comparator to value extractor: more compilers will see what is actually going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]: /messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Attachments:

v1-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v1-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From 39354c4ee3bdf837b0c9a09aafaf64640a0a086e Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v1] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 12 ------------
 contrib/intarray/_int_gist.c |  2 +-
 contrib/intarray/_int_op.c   | 19 +++++++++----------
 contrib/intarray/_int_tool.c | 12 ------------
 src/include/port.h           |  2 ++
 src/port/qsort.c             | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd3682..5225c9090a8 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -176,16 +176,4 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
-/* sort, either ascending or descending */
-#define QSORT(a, direction) \
-	do { \
-		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
-	} while(0)
-
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index a09b7fa812c..d39e40c66aa 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -150,7 +150,7 @@ g_int_union(PG_FUNCTION_ARGS)
 		ptr += nel;
 	}
 
-	QSORT(res, 1);
+	sort_int32_asc(ARRPTR(res), ARRNELEMS(res));
 	res = _int_unique(res);
 	*size = VARSIZE(res);
 	PG_RETURN_POINTER(res);
diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c
index 5b164f6788f..34d3aa183f8 100644
--- a/contrib/intarray/_int_op.c
+++ b/contrib/intarray/_int_op.c
@@ -198,7 +198,6 @@ sort(PG_FUNCTION_ARGS)
 	text	   *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_PP(1) : NULL;
 	int32		dc = (dirstr) ? VARSIZE_ANY_EXHDR(dirstr) : 0;
 	char	   *d = (dirstr) ? VARDATA_ANY(dirstr) : NULL;
-	int			dir = -1;
 
 	CHECKARRVALID(a);
 	if (ARRNELEMS(a) < 2)
@@ -208,18 +207,18 @@ sort(PG_FUNCTION_ARGS)
 						   && (d[0] == 'A' || d[0] == 'a')
 						   && (d[1] == 'S' || d[1] == 's')
 						   && (d[2] == 'C' || d[2] == 'c')))
-		dir = 1;
+		sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	else if (dc == 4
 			 && (d[0] == 'D' || d[0] == 'd')
 			 && (d[1] == 'E' || d[1] == 'e')
 			 && (d[2] == 'S' || d[2] == 's')
 			 && (d[3] == 'C' || d[3] == 'c'))
-		dir = 0;
-	if (dir == -1)
+		sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
+	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("second parameter must be \"ASC\" or \"DESC\"")));
-	QSORT(a, dir);
+
 	PG_RETURN_POINTER(a);
 }
 
@@ -229,7 +228,7 @@ sort_asc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -239,7 +238,7 @@ sort_desc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 0);
+	sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -381,7 +380,7 @@ intset_union_elem(PG_FUNCTION_ARGS)
 
 	result = intarray_add_elem(a, PG_GETARG_INT32(1));
 	PG_FREE_IF_COPY(a, 0);
-	QSORT(result, 1);
+	sort_int32_asc(ARRPTR(result), ARRNELEMS(result));
 	PG_RETURN_POINTER(_int_unique(result));
 }
 
@@ -403,10 +402,10 @@ intset_subtract(PG_FUNCTION_ARGS)
 	CHECKARRVALID(a);
 	CHECKARRVALID(b);
 
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	a = _int_unique(a);
 	ca = ARRNELEMS(a);
-	QSORT(b, 1);
+	sort_int32_asc(ARRPTR(b), ARRNELEMS(b));
 	b = _int_unique(b);
 	cb = ARRNELEMS(b);
 	result = new_intArrayType(ca);
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c8422..e83c6aadc6c 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -393,15 +393,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
diff --git a/src/include/port.h b/src/include/port.h
index ae115d2d970..b48194cb004 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -445,6 +445,8 @@ extern bool pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32_asc(int32 *base, size_t nel);
+extern void sort_int32_desc(int32 *base, size_t nel);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 7879e6cd563..5175c8a6dde 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -20,3 +20,38 @@ pg_qsort_strcmp(const void *a, const void *b)
 {
 	return strcmp(*(const char *const *) a, *(const char *const *) b);
 }
+
+static inline int
+sort_int32_asc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return -1;
+	if (*a > *b)
+		return 1;
+	return 0;
+}
+
+#define ST_SORT sort_int32_asc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_asc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+static inline int
+sort_int32_desc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return 1;
+	if (*a > *b)
+		return -1;
+	return 0;
+}
+#define ST_SORT sort_int32_desc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_desc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
-- 
2.37.1 (Apple Git-137.1)

#2Ranier Vilela
ranier.vf@gmail.com
In reply to: Andrey M. Borodin (#1)
Re: Sort functions with specialized comparators

Em sáb., 18 de mai. de 2024 às 15:52, Andrey M. Borodin <
x4mmm@yandex-team.ru> escreveu:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber, Oid
etc. But this sorting routines never show up in perf top or something like
that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Makes sense.

Regarding the patch.
You could change the style to:

+sort_int32_asc_cmp(const int32 *a, const int32 *b)
+sort_int32_desc_cmp(const int32 *a, const int32 *b)

We must use const in all parameters that can be const.

best regards,
Ranier Vilela

#3Stepan Neretin
sncfmgg@gmail.com
In reply to: Andrey M. Borodin (#1)
6 attachment(s)
Re: Sort functions with specialized comparators

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would introduce
similar optimizations when working with a list of integers or object
identifiers. Additionally, my patch includes an extension for benchmarking,
which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

Show quoted text

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber, Oid
etc. But this sorting routines never show up in perf top or something like
that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Attachments:

v42-0006-Implemented-benchmarking-for-optimized-sorting.patchtext/x-patch; charset=US-ASCII; name=v42-0006-Implemented-benchmarking-for-optimized-sorting.patchDownload
From 74bad4bbcff9ea4a9a68f91618c84854dab24701 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 01:29:42 +0700
Subject: [PATCH v42 6/6] Implemented benchmarking for optimized sorting

This commit adds benchmarking functions to compare the performance of two list sorting operations: bench_int_sort and bench_oid_sort. These functions measure the execution time of sorting lists of integers and OIDs, respectively, using different algorithms (list_sort and custom sorting functions). Random lists of specified sizes are generated, sorted using both methods, and their execution times are recorded. The percentage difference in execution time between the two methods is also calculated. This commit aims to provide insights into the efficiency of the sorting algorithms used.
---
 contrib/Makefile                              |   1 +
 contrib/bench_sort_improvements/Makefile      |  20 ++++
 contrib/bench_sort_improvements/bench.c       | 105 ++++++++++++++++++
 .../bench_sort_improvements--1.0.sql          |   3 +
 .../bench_sort_improvements.control           |   5 +
 5 files changed, 134 insertions(+)
 create mode 100644 contrib/bench_sort_improvements/Makefile
 create mode 100644 contrib/bench_sort_improvements/bench.c
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements.control

diff --git a/contrib/Makefile b/contrib/Makefile
index abd780f277..a1ee9defc2 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -10,6 +10,7 @@ SUBDIRS = \
 		auto_explain	\
 		basic_archive	\
 		basebackup_to_shell	\
+		bench_sort_improvements \
 		bloom		\
 		btree_gin	\
 		btree_gist	\
diff --git a/contrib/bench_sort_improvements/Makefile b/contrib/bench_sort_improvements/Makefile
new file mode 100644
index 0000000000..46458ee76c
--- /dev/null
+++ b/contrib/bench_sort_improvements/Makefile
@@ -0,0 +1,20 @@
+MODULE_big = bench_sort_improvements
+
+OBJS = \
+	$(WIN32RES) \
+	bench.o
+
+EXTENSION = bench_sort_improvements
+
+DATA = bench_sort_improvements--1.0.sql
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/bench_sort_improvements
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/bench_sort_improvements/bench.c b/contrib/bench_sort_improvements/bench.c
new file mode 100644
index 0000000000..77d5c7fa37
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench.c
@@ -0,0 +1,105 @@
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "nodes/pg_list.h"
+#include <limits.h>
+#include <time.h>
+
+
+PG_MODULE_MAGIC;
+
+Datum bench_int_sort(PG_FUNCTION_ARGS);
+Datum bench_oid_sort(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(bench_oid_sort);
+PG_FUNCTION_INFO_V1(bench_int_sort);
+
+Datum
+bench_oid_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    Oid random_oid;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_oid = (Oid) (random() % (UINT_MAX - 1) + 1); 
+        list_first = lappend_oid(list_first, random_oid);
+        list_second = lappend_oid(list_second, random_oid);
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_oid_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_oid_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+Datum
+bench_int_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    int random_int;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_int = rand(); 
+        list_first = lappend_int(list_first, random_int); 
+        list_second = lappend_int(list_second, random_int); 
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_int_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_int_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+void
+_PG_init()
+{
+    srand(time(NULL));
+}
\ No newline at end of file
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
new file mode 100644
index 0000000000..97b75d368d
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
@@ -0,0 +1,3 @@
+create function bench_oid_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_oid_sort' LANGUAGE C;
+
+create function bench_int_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int_sort' LANGUAGE C;
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements.control b/contrib/bench_sort_improvements/bench_sort_improvements.control
new file mode 100644
index 0000000000..7dc05056ac
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements.control
@@ -0,0 +1,5 @@
+# fuzzystrmatch extension
+comment = 'test extension'
+default_version = '1.0'
+module_pathname = '$libdir/bench_sort_improvements'
+relocatable = true
-- 
2.34.1

v42-0002-Optimized-Oid-List-Sorting-by-using-template-sor.patchtext/x-patch; charset=US-ASCII; name=v42-0002-Optimized-Oid-List-Sorting-by-using-template-sor.patchDownload
From 32f967a10a2dd6a9f68aa85717ab11ab83f03298 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:04:42 +0700
Subject: [PATCH v42 2/6] Optimized Oid List Sorting by using template sorting
 algorithm

Optimized the sorting of lists containing Oids by utilizing a custom sort template. This enhancement introduces a specialized sorting function sort_list_oids defined through macros and included from sort_template.h. The new function improves performance by directly comparing Oids and sorting the list in-place.

Changes:
- Defined ST_SORT, ST_ELEMENT_TYPE, ST_COMPARE, ST_SCOPE, and ST_DEFINE macros for sort_list_oids.
- Included sort_template.h to leverage the template-based sorting mechanism.
- Implemented list_oid_sort function to sort lists with Oid type.

This optimization is expected to provide better sorting efficiency for lists containing Oids, contributing to overall system performance improvements.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index e2615ab105..e10ce545ad 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1707,3 +1707,17 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
 
 	return pg_cmp_u32(v1, v2);
 }
+
+#define ST_SORT sort_list_oids
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_oid_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with Oid type optimization.
+ */
+void list_oid_sort(List *data){
+   sort_list_oids(list_head(data), list_length(data));
+}
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 52df93759f..88aa1ebea9 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -679,6 +679,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
+extern void list_oid_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.34.1

v42-0004-Optimized-Int-List-Sorting-by-using-template-sor.patchtext/x-patch; charset=US-ASCII; name=v42-0004-Optimized-Int-List-Sorting-by-using-template-sor.patchDownload
From beb5b92d04075b0539f2a6965f58d6a63ae2898e Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:14:18 +0700
Subject: [PATCH v42 4/6] Optimized Int List Sorting by using template sorting
 algorithm

This commit optimizes the sorting of integer lists by implementing a custom sort template algorithm. It introduces a specialized sorting function, `list_int_sort`, defined through macros and included from `sort_template.h`. The enhancement aims to improve performance by enabling direct comparison of integers and sorting the list in-place.

Changes:
- Defined macros `ST_SORT`, `ST_ELEMENT_TYPE`, `ST_COMPARE`, `ST_SCOPE`, and `ST_DEFINE` for the `list_int_sort` function.
- Included `sort_template.h` to leverage the template-based sorting mechanism.
- Implemented the `list_int_sort` function to efficiently sort integer lists.

This optimization is expected to provide better sorting efficiency for lists containing Ints, contributing to overall system performance improvements.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index e10ce545ad..197ce1b8f4 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1721,3 +1721,17 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
 void list_oid_sort(List *data){
    sort_list_oids(list_head(data), list_length(data));
 }
+
+#define ST_SORT sort_list_ints
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_int_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with int type optimization.
+ */
+void list_int_sort(List *data){
+   sort_list_ints(list_head(data), list_length(data));
+}
\ No newline at end of file
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 88aa1ebea9..7757b1cdf0 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -680,6 +680,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 extern void list_oid_sort(List *list);
+extern void list_int_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.34.1

v42-0005-Enhanced-Sorting-Efficiency-for-Integer-Lists.patchtext/x-patch; charset=US-ASCII; name=v42-0005-Enhanced-Sorting-Efficiency-for-Integer-Lists.patchDownload
From d36ac2636e639520f163475a7c08c0cf8b97b866 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:32:44 +0700
Subject: [PATCH v42 5/6] Enhanced Sorting Efficiency for Integer Lists

Refactored the sorting of lists containing integers in the `expand_grouping_sets` function within the `parse_agg.c` file. Replaced the generic sorting function with a custom optimized function tailored for integer types. This change leverages a custom sort template to enhance performance specifically for integer types.

Details:
- Updated the sorting of each groupset individually within the `expand_grouping_sets` function by replacing the loop-based `list_sort` with a single call to `list_int_sort`.
- Updated the comparison function `list_int_cmp` to `int_cmp` within the `expand_grouping_sets` function.
- Updated the `expand_grouping_sets` function to use `sort_list_ints` in two locations where sorting is performed.

This refactor aims to improve sorting efficiency for integer lists in the context of expanding grouping sets within the `parse_agg.c` file, reducing processing time and enhancing overall system performance.
---
 src/backend/parser/parse_agg.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index bee7d8346a..102131341a 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1869,8 +1869,7 @@ expand_grouping_sets(List *groupingSets, bool groupDistinct, int limit)
 		List	   *prev;
 
 		/* Sort each groupset individually */
-		foreach(cell, result)
-			list_sort(lfirst(cell), list_int_cmp);
+		list_int_sort(result);
 
 		/* Now sort the list of groupsets by length and contents */
 		list_sort(result, cmp_list_len_contents_asc);
-- 
2.34.1

v42-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchtext/x-patch; charset=US-ASCII; name=v42-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchDownload
From d82427a4c1db68c59e1d5a35f25f0391a6ae68e6 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:04:59 +0700
Subject: [PATCH v42 3/6] Enhanced Sorting Efficiency for Oid Lists

Refactored the sorting of lists containing Oids by replacing the generic function
with the custom optimized function. This change leverages a custom sort template
to enhance performance specifically for Oid types.

Details:
- Updated to use instead of.
- Updated to use instead of.
- Updated to use instead of in two locations.

This refactor aims to improve sorting efficiency for Oid lists, reducing
processing time and enhancing overall system performance.
---
 src/backend/catalog/heap.c           | 2 +-
 src/backend/catalog/pg_publication.c | 2 +-
 src/backend/utils/cache/relcache.c   | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a122bbffce..88e4ada970 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3303,7 +3303,7 @@ restart:
 	list_free(oids);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..faf70ec8c7 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -746,7 +746,7 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
 	table_close(pubrelsrel, AccessShareLock);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 35dbb87ae3..21b3f3c92f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4873,7 +4873,7 @@ RelationGetIndexList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
@@ -4965,7 +4965,7 @@ RelationGetStatExtList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-- 
2.34.1

v42-0001-Use-specialized-sort-facilities.patchtext/x-patch; charset=US-ASCII; name=v42-0001-Use-specialized-sort-facilities.patchDownload
From 530911dd2e1a60209421551bc24b5d72628050e5 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v42 1/6] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 12 ------------
 contrib/intarray/_int_gist.c |  2 +-
 contrib/intarray/_int_op.c   | 19 +++++++++----------
 contrib/intarray/_int_tool.c | 12 ------------
 src/include/port.h           |  2 ++
 src/port/qsort.c             | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..5225c9090a 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -176,16 +176,4 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
-/* sort, either ascending or descending */
-#define QSORT(a, direction) \
-	do { \
-		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
-	} while(0)
-
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index a09b7fa812..d39e40c66a 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -150,7 +150,7 @@ g_int_union(PG_FUNCTION_ARGS)
 		ptr += nel;
 	}
 
-	QSORT(res, 1);
+	sort_int32_asc(ARRPTR(res), ARRNELEMS(res));
 	res = _int_unique(res);
 	*size = VARSIZE(res);
 	PG_RETURN_POINTER(res);
diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c
index 5b164f6788..34d3aa183f 100644
--- a/contrib/intarray/_int_op.c
+++ b/contrib/intarray/_int_op.c
@@ -198,7 +198,6 @@ sort(PG_FUNCTION_ARGS)
 	text	   *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_PP(1) : NULL;
 	int32		dc = (dirstr) ? VARSIZE_ANY_EXHDR(dirstr) : 0;
 	char	   *d = (dirstr) ? VARDATA_ANY(dirstr) : NULL;
-	int			dir = -1;
 
 	CHECKARRVALID(a);
 	if (ARRNELEMS(a) < 2)
@@ -208,18 +207,18 @@ sort(PG_FUNCTION_ARGS)
 						   && (d[0] == 'A' || d[0] == 'a')
 						   && (d[1] == 'S' || d[1] == 's')
 						   && (d[2] == 'C' || d[2] == 'c')))
-		dir = 1;
+		sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	else if (dc == 4
 			 && (d[0] == 'D' || d[0] == 'd')
 			 && (d[1] == 'E' || d[1] == 'e')
 			 && (d[2] == 'S' || d[2] == 's')
 			 && (d[3] == 'C' || d[3] == 'c'))
-		dir = 0;
-	if (dir == -1)
+		sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
+	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("second parameter must be \"ASC\" or \"DESC\"")));
-	QSORT(a, dir);
+
 	PG_RETURN_POINTER(a);
 }
 
@@ -229,7 +228,7 @@ sort_asc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -239,7 +238,7 @@ sort_desc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 0);
+	sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -381,7 +380,7 @@ intset_union_elem(PG_FUNCTION_ARGS)
 
 	result = intarray_add_elem(a, PG_GETARG_INT32(1));
 	PG_FREE_IF_COPY(a, 0);
-	QSORT(result, 1);
+	sort_int32_asc(ARRPTR(result), ARRNELEMS(result));
 	PG_RETURN_POINTER(_int_unique(result));
 }
 
@@ -403,10 +402,10 @@ intset_subtract(PG_FUNCTION_ARGS)
 	CHECKARRVALID(a);
 	CHECKARRVALID(b);
 
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	a = _int_unique(a);
 	ca = ARRNELEMS(a);
-	QSORT(b, 1);
+	sort_int32_asc(ARRPTR(b), ARRNELEMS(b));
 	b = _int_unique(b);
 	cb = ARRNELEMS(b);
 	result = new_intArrayType(ca);
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..e83c6aadc6 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -393,15 +393,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
diff --git a/src/include/port.h b/src/include/port.h
index ae115d2d97..b48194cb00 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -445,6 +445,8 @@ extern bool pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32_asc(int32 *base, size_t nel);
+extern void sort_int32_desc(int32 *base, size_t nel);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 7879e6cd56..5175c8a6dd 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -20,3 +20,38 @@ pg_qsort_strcmp(const void *a, const void *b)
 {
 	return strcmp(*(const char *const *) a, *(const char *const *) b);
 }
+
+static inline int
+sort_int32_asc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return -1;
+	if (*a > *b)
+		return 1;
+	return 0;
+}
+
+#define ST_SORT sort_int32_asc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_asc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+static inline int
+sort_int32_desc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return 1;
+	if (*a > *b)
+		return -1;
+	return 0;
+}
+#define ST_SORT sort_int32_desc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_desc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
-- 
2.34.1

#4Stepan Neretin
sncfmgg@gmail.com
In reply to: Stepan Neretin (#3)
12 attachment(s)
Re: Sort functions with specialized comparators

On Sat, Jun 8, 2024 at 1:50 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would introduce
similar optimizations when working with a list of integers or object
identifiers. Additionally, my patch includes an extension for benchmarking,
which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber,
Oid etc. But this sorting routines never show up in perf top or something
like that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Hello all.

I have decided to explore more areas in which I can optimize and have added
two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

postgres=#

Best regards, Stepan Neretin.

Attachments:

v42-0008-Refactor-sorting-of-attribute-numbers-in-pg_publ.patchtext/x-patch; charset=US-ASCII; name=v42-0008-Refactor-sorting-of-attribute-numbers-in-pg_publ.patchDownload
From a3c03ee1b4f6d94d6bf2dc8700c30349271ef9b3 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:02:48 +0700
Subject: [PATCH v42 08/12] Refactor sorting of attribute numbers in
 pg_publication.c and statscmds.c

This commit refactors the sorting of attribute numbers in two modules:
pg_publication.c and statscmds.c. Instead of using the qsort function
with a custom compare function, it utilizes the recently introduced
sort_int_16_arr function, which offers enhanced performance for sorting
int16 arrays.

  Details:
- Replaced qsort calls with sort_int_16_arr for sorting attribute numbers.
- Improved efficiency by utilizing the sorting template for int16 arrays.

This change ensures consistency in sorting methods and enhances sorting
performance in relevant modules.
---
 src/backend/catalog/pg_publication.c | 2 +-
 src/backend/commands/statscmds.c     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 8518582b76..196f696c1e 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -540,7 +540,7 @@ publication_translate_columns(Relation targetrel, List *columns,
 	}
 
 	/* Be tidy, so that the catalog representation is always sorted */
-	qsort(attarray, n, sizeof(AttrNumber), compare_int16);
+	sort_int_16_arr(attarray, n);
 
 	*natts = n;
 	*attrs = attarray;
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 14d9b035b7..e448b2d601 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -392,7 +392,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	 * it does not hurt (it does not matter for the contents, unlike for
 	 * indexes, for example).
 	 */
-	qsort(attnums, nattnums, sizeof(int16), compare_int16);
+	sort_int_16_arr(attnums, nattnums);
 
 	/*
 	 * Check for duplicates in the list of columns. The attnums are sorted so
-- 
2.34.1

v42-0009-Introduce-benchmarking-function-for-int16-array-.patchtext/x-patch; charset=US-ASCII; name=v42-0009-Introduce-benchmarking-function-for-int16-array-.patchDownload
From fa20e159b527ec0bdd4772d3d9f5d8a7a981a6fb Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:07:01 +0700
Subject: [PATCH v42 09/12] Introduce benchmarking function for int16 array
 sorting

This commit adds a benchmarking function, bench_int16_sort, to compare
the performance of two sorting methods for int16 arrays: the standard
qsort function and the optimized sort_int_16_arr function. The benchmark
generates two arrays of random int16 values, sorts each array using
both methods, and measures the time taken for each sorting operation.
Additionally, it calculates the percentage difference in execution time
between the two methods.

  Details:
- Added a benchmarking function to compare sorting methods for int16 arrays.
- Generates random int16 arrays for benchmarking.
- Measures execution time for sorting using both qsort and optimized sort_int_16_arr.
- Calculates percentage difference in execution time between methods.
---
 contrib/bench_sort_improvements/bench.c       | 55 +++++++++++++++++++
 .../bench_sort_improvements--1.0.sql          |  2 +
 2 files changed, 57 insertions(+)

diff --git a/contrib/bench_sort_improvements/bench.c b/contrib/bench_sort_improvements/bench.c
index 77d5c7fa37..c0d4ec86e1 100644
--- a/contrib/bench_sort_improvements/bench.c
+++ b/contrib/bench_sort_improvements/bench.c
@@ -9,10 +9,12 @@
 PG_MODULE_MAGIC;
 
 Datum bench_int_sort(PG_FUNCTION_ARGS);
+Datum bench_int16_sort(PG_FUNCTION_ARGS);
 Datum bench_oid_sort(PG_FUNCTION_ARGS);
 
 PG_FUNCTION_INFO_V1(bench_oid_sort);
 PG_FUNCTION_INFO_V1(bench_int_sort);
+PG_FUNCTION_INFO_V1(bench_int16_sort);
 
 Datum
 bench_oid_sort(PG_FUNCTION_ARGS)
@@ -98,6 +100,59 @@ bench_int_sort(PG_FUNCTION_ARGS)
     PG_RETURN_TEXT_P(cstring_to_text(result_message));
 }
 
+/*
+stupid copy for tests
+*/
+static int
+compare_int16(const void *a, const void *b)
+{
+	int			av = *(const int16 *) a;
+	int			bv = *(const int16 *) b;
+
+	/* this can't overflow if int is wider than int16 */
+	return (av - bv);
+}
+
+Datum
+bench_int16_sort(PG_FUNCTION_ARGS)
+{
+    int32 arr_size = PG_GETARG_INT32(0);
+    int16 *arr_first = (int16 *)palloc(arr_size * sizeof(int16));
+    int16 *arr_second = (int16 *)palloc(arr_size * sizeof(int16));
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < arr_size; i++)
+    {
+        arr_first[i] = (int16)rand();
+        arr_second[i] = (int16)rand();
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    qsort(arr_first, arr_size, sizeof(int16), compare_int16);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    sort_int_16_arr(arr_second, arr_size);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    pfree(arr_first);
+    pfree(arr_second);
+    
+    result_message = psprintf("Time taken by usual sort: %ld ns, Time taken by optimized sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
 void
 _PG_init()
 {
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
index 97b75d368d..bff8a6ab59 100644
--- a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
+++ b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
@@ -1,3 +1,5 @@
 create function bench_oid_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_oid_sort' LANGUAGE C;
 
 create function bench_int_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int_sort' LANGUAGE C;
+
+create function bench_int16_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int16_sort' LANGUAGE C;
-- 
2.34.1

v42-0010-Implement-Sorting-Template-for-float8-Arrays.patchtext/x-patch; charset=US-ASCII; name=v42-0010-Implement-Sorting-Template-for-float8-Arrays.patchDownload
From b47eeaaef16377bb55ba88ef78d05655703ab658 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:39:03 +0700
Subject: [PATCH v42 10/12] Implement Sorting Template for float8 Arrays

Introduce a sorting template for float8 arrays in geo_spgist.c to boost performance.
---
 src/backend/utils/adt/geo_spgist.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 51378dca5b..cbd45b3933 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -100,6 +100,17 @@ compareDoubles(const void *a, const void *b)
 	return (x > y) ? 1 : -1;
 }
 
+ /*
+ * Instantiating a Sorting Template for float8 Arrays
+ * enhancing speed performance.
+ */
+#define ST_SORT sort_float8_arr
+#define ST_ELEMENT_TYPE float8
+#define ST_COMPARE(a, b) compareDoubles(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 typedef struct
 {
 	float8		low;
-- 
2.34.1

v42-0012-Add-benchmark-comparing-float8-sorting-methods.patchtext/x-patch; charset=US-ASCII; name=v42-0012-Add-benchmark-comparing-float8-sorting-methods.patchDownload
From 4341f7ff48004bef2fe9707f1a03236d0bd7c4e7 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:42:03 +0700
Subject: [PATCH v42 12/12] Add benchmark comparing float8 sorting methods

Introduce `bench_float8_sort` to compare qsort with `sort_float8_arr`. It generates random float8 arrays, times sorting, and calculates performance difference.
---
 contrib/bench_sort_improvements/bench.c       | 70 +++++++++++++++++++
 .../bench_sort_improvements--1.0.sql          |  2 +
 2 files changed, 72 insertions(+)

diff --git a/contrib/bench_sort_improvements/bench.c b/contrib/bench_sort_improvements/bench.c
index c0d4ec86e1..a4088783c6 100644
--- a/contrib/bench_sort_improvements/bench.c
+++ b/contrib/bench_sort_improvements/bench.c
@@ -10,11 +10,13 @@ PG_MODULE_MAGIC;
 
 Datum bench_int_sort(PG_FUNCTION_ARGS);
 Datum bench_int16_sort(PG_FUNCTION_ARGS);
+Datum bench_float8_sort(PG_FUNCTION_ARGS);
 Datum bench_oid_sort(PG_FUNCTION_ARGS);
 
 PG_FUNCTION_INFO_V1(bench_oid_sort);
 PG_FUNCTION_INFO_V1(bench_int_sort);
 PG_FUNCTION_INFO_V1(bench_int16_sort);
+PG_FUNCTION_INFO_V1(bench_float8_sort);
 
 Datum
 bench_oid_sort(PG_FUNCTION_ARGS)
@@ -153,6 +155,74 @@ bench_int16_sort(PG_FUNCTION_ARGS)
     PG_RETURN_TEXT_P(cstring_to_text(result_message));
 }
 
+double inline rand_double() {
+    return (double)rand() / RAND_MAX; 
+}
+
+/*
+stupid copy for tests
+*/
+static int
+compareDoubles(const void *a, const void *b)
+{
+	float8		x = *(float8 *) a;
+	float8		y = *(float8 *) b;
+
+	if (x == y)
+		return 0;
+	return (x > y) ? 1 : -1;
+}
+
+/*
+stupid copy for tests
+*/
+#define ST_SORT sort_float8_arr
+#define ST_ELEMENT_TYPE float8
+#define ST_COMPARE(a, b) compareDoubles(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+Datum
+bench_float8_sort(PG_FUNCTION_ARGS)
+{
+    int32 arr_size = PG_GETARG_INT32(0);
+    float8 *arr_first = (float8 *)palloc(arr_size * sizeof(float8));
+    float8 *arr_second = (float8 *)palloc(arr_size * sizeof(float8));
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < arr_size; i++)
+    {
+        arr_first[i] = rand_double();
+        arr_second[i] = rand_double();
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    qsort(arr_first, arr_size, sizeof(float8), compareDoubles);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    sort_float8_arr(arr_second, arr_size);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    pfree(arr_first);
+    pfree(arr_second);
+    
+    result_message = psprintf("Time taken by usual sort: %ld ns, Time taken by optimized sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
 void
 _PG_init()
 {
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
index bff8a6ab59..5c51a5ef81 100644
--- a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
+++ b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
@@ -3,3 +3,5 @@ create function bench_oid_sort(integer) returns text AS 'MODULE_PATHNAME', 'benc
 create function bench_int_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int_sort' LANGUAGE C;
 
 create function bench_int16_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int16_sort' LANGUAGE C;
+
+create function bench_float8_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_float8_sort' LANGUAGE C;
-- 
2.34.1

v42-0011-Optimize-box-quad-picksplit-with-float8-array-so.patchtext/x-patch; charset=US-ASCII; name=v42-0011-Optimize-box-quad-picksplit-with-float8-array-so.patchDownload
From 8c3b665a379e7b0bd85ae9d58e66e9f8d289eed8 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:40:56 +0700
Subject: [PATCH v42 11/12] Optimize box quad picksplit with float8 array
 sorting

Replace qsort calls with the new sorting template `sort_float8_arr` for better performance in spg_box_quad_picksplit function.
---
 src/backend/utils/adt/geo_spgist.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index cbd45b3933..b740e89623 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -472,10 +472,10 @@ spg_box_quad_picksplit(PG_FUNCTION_ARGS)
 		highYs[i] = box->high.y;
 	}
 
-	qsort(lowXs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(highXs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(lowYs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(highYs, in->nTuples, sizeof(float8), compareDoubles);
+	sort_float8_arr(lowXs, in->nTuples);
+	sort_float8_arr(highXs, in->nTuples);
+	sort_float8_arr(lowYs, in->nTuples);
+	sort_float8_arr(highYs, in->nTuples);
 
 	median = in->nTuples / 2;
 
-- 
2.34.1

v42-0005-Enhanced-Sorting-Efficiency-for-Integer-Lists.patchtext/x-patch; charset=US-ASCII; name=v42-0005-Enhanced-Sorting-Efficiency-for-Integer-Lists.patchDownload
From d36ac2636e639520f163475a7c08c0cf8b97b866 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:32:44 +0700
Subject: [PATCH v42 05/12] Enhanced Sorting Efficiency for Integer Lists

Refactored the sorting of lists containing integers in the `expand_grouping_sets` function within the `parse_agg.c` file. Replaced the generic sorting function with a custom optimized function tailored for integer types. This change leverages a custom sort template to enhance performance specifically for integer types.

Details:
- Updated the sorting of each groupset individually within the `expand_grouping_sets` function by replacing the loop-based `list_sort` with a single call to `list_int_sort`.
- Updated the comparison function `list_int_cmp` to `int_cmp` within the `expand_grouping_sets` function.
- Updated the `expand_grouping_sets` function to use `sort_list_ints` in two locations where sorting is performed.

This refactor aims to improve sorting efficiency for integer lists in the context of expanding grouping sets within the `parse_agg.c` file, reducing processing time and enhancing overall system performance.
---
 src/backend/parser/parse_agg.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index bee7d8346a..102131341a 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1869,8 +1869,7 @@ expand_grouping_sets(List *groupingSets, bool groupDistinct, int limit)
 		List	   *prev;
 
 		/* Sort each groupset individually */
-		foreach(cell, result)
-			list_sort(lfirst(cell), list_int_cmp);
+		list_int_sort(result);
 
 		/* Now sort the list of groupsets by length and contents */
 		list_sort(result, cmp_list_len_contents_asc);
-- 
2.34.1

v42-0007-Consolidate-and-optimize-int16-array-sorting.patchtext/x-patch; charset=US-ASCII; name=v42-0007-Consolidate-and-optimize-int16-array-sorting.patchDownload
From abbca3581624b70709bfcfcaad2343a2d78df78b Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Tue, 11 Jun 2024 12:00:11 +0700
Subject: [PATCH v42 07/12] Consolidate and optimize int16 array sorting

This commit optimizes int16 array sorting by consolidating the
compare_int16 function into a single location and removing duplicate
definitions across multiple files. Additionally, it introduces a
sorting template to enhance sorting performance.

  Changes:
- Consolidated the compare_int16 function into a single location.
- Removed duplicate definitions of compare_int16.
- Introduced a sorting template for int16 arrays to improve performance.
---
 src/backend/catalog/pg_publication.c | 11 ----------
 src/backend/commands/statscmds.c     | 12 ----------
 src/backend/utils/adt/numutils.c     | 33 ++++++++++++++++++++++++++++
 src/include/utils/builtins.h         |  1 +
 4 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index faf70ec8c7..8518582b76 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -476,17 +476,6 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	return myself;
 }
 
-/* qsort comparator for attnums */
-static int
-compare_int16(const void *a, const void *b)
-{
-	int			av = *(const int16 *) a;
-	int			bv = *(const int16 *) b;
-
-	/* this can't overflow if int is wider than int16 */
-	return (av - bv);
-}
-
 /*
  * Translate a list of column names to an array of attribute numbers
  * and a Bitmapset with them; verify that each attribute is appropriate
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..14d9b035b7 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -43,18 +43,6 @@ static char *ChooseExtendedStatisticName(const char *name1, const char *name2,
 										 const char *label, Oid namespaceid);
 static char *ChooseExtendedStatisticNameAddition(List *exprs);
 
-
-/* qsort comparator for the attnums in CreateStatistics */
-static int
-compare_int16(const void *a, const void *b)
-{
-	int			av = *(const int16 *) a;
-	int			bv = *(const int16 *) b;
-
-	/* this can't overflow if int is wider than int16 */
-	return (av - bv);
-}
-
 /*
  *		CREATE STATISTICS
  */
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index adc1e8a4cb..f538d1be2b 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -1312,3 +1312,36 @@ pg_ultostr(char *str, uint32 value)
 
 	return str + len;
 }
+
+/*
+ * compare_int16: Compare two int16 values.
+ *
+ * This function compares two int16 values passed as pointers. It first
+ * dereferences the pointers to obtain the actual int16 values, then
+ * subtracts one from the other to determine their relative ordering.
+ * Note:
+ *   - This function assumes that the underlying architecture represents
+ *     int16 values using a two's complement representation.
+ *   - It does not perform overflow checking, assuming that 'int' is
+ *     wider than 'int16'.
+ */
+static int
+compare_int16(const void *a, const void *b)
+{
+	int			av = *(const int16 *) a;
+	int			bv = *(const int16 *) b;
+
+	/* this can't overflow if int is wider than int16 */
+	return (av - bv);
+}
+
+ /* 
+ * Instantiating a Sorting Template for int16 Arrays
+ * enhancing speed performance.
+ */
+#define ST_SORT sort_int_16_arr
+#define ST_ELEMENT_TYPE int16
+#define ST_COMPARE(a, b) compare_int16(a, b)
+#define ST_SCOPE extern
+#define ST_DEFINE
+#include <lib/sort_template.h>
\ No newline at end of file
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 359c570f23..8ddbb8b142 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -42,6 +42,7 @@ extern uint64 hex_decode_safe(const char *src, size_t len, char *dst,
 
 /* int.c */
 extern int2vector *buildint2vector(const int16 *int2s, int n);
+extern void sort_int_16_arr(int16 *arr, size_t n);
 
 /* name.c */
 extern void namestrcpy(Name name, const char *str);
-- 
2.34.1

v42-0004-Optimized-Int-List-Sorting-by-using-template-sor.patchtext/x-patch; charset=US-ASCII; name=v42-0004-Optimized-Int-List-Sorting-by-using-template-sor.patchDownload
From beb5b92d04075b0539f2a6965f58d6a63ae2898e Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:14:18 +0700
Subject: [PATCH v42 04/12] Optimized Int List Sorting by using template
 sorting algorithm

This commit optimizes the sorting of integer lists by implementing a custom sort template algorithm. It introduces a specialized sorting function, `list_int_sort`, defined through macros and included from `sort_template.h`. The enhancement aims to improve performance by enabling direct comparison of integers and sorting the list in-place.

Changes:
- Defined macros `ST_SORT`, `ST_ELEMENT_TYPE`, `ST_COMPARE`, `ST_SCOPE`, and `ST_DEFINE` for the `list_int_sort` function.
- Included `sort_template.h` to leverage the template-based sorting mechanism.
- Implemented the `list_int_sort` function to efficiently sort integer lists.

This optimization is expected to provide better sorting efficiency for lists containing Ints, contributing to overall system performance improvements.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index e10ce545ad..197ce1b8f4 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1721,3 +1721,17 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
 void list_oid_sort(List *data){
    sort_list_oids(list_head(data), list_length(data));
 }
+
+#define ST_SORT sort_list_ints
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_int_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with int type optimization.
+ */
+void list_int_sort(List *data){
+   sort_list_ints(list_head(data), list_length(data));
+}
\ No newline at end of file
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 88aa1ebea9..7757b1cdf0 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -680,6 +680,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 extern void list_oid_sort(List *list);
+extern void list_int_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.34.1

v42-0006-Implemented-benchmarking-for-optimized-sorting.patchtext/x-patch; charset=US-ASCII; name=v42-0006-Implemented-benchmarking-for-optimized-sorting.patchDownload
From 74bad4bbcff9ea4a9a68f91618c84854dab24701 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 01:29:42 +0700
Subject: [PATCH v42 06/12] Implemented benchmarking for optimized sorting

This commit adds benchmarking functions to compare the performance of two list sorting operations: bench_int_sort and bench_oid_sort. These functions measure the execution time of sorting lists of integers and OIDs, respectively, using different algorithms (list_sort and custom sorting functions). Random lists of specified sizes are generated, sorted using both methods, and their execution times are recorded. The percentage difference in execution time between the two methods is also calculated. This commit aims to provide insights into the efficiency of the sorting algorithms used.
---
 contrib/Makefile                              |   1 +
 contrib/bench_sort_improvements/Makefile      |  20 ++++
 contrib/bench_sort_improvements/bench.c       | 105 ++++++++++++++++++
 .../bench_sort_improvements--1.0.sql          |   3 +
 .../bench_sort_improvements.control           |   5 +
 5 files changed, 134 insertions(+)
 create mode 100644 contrib/bench_sort_improvements/Makefile
 create mode 100644 contrib/bench_sort_improvements/bench.c
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements.control

diff --git a/contrib/Makefile b/contrib/Makefile
index abd780f277..a1ee9defc2 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -10,6 +10,7 @@ SUBDIRS = \
 		auto_explain	\
 		basic_archive	\
 		basebackup_to_shell	\
+		bench_sort_improvements \
 		bloom		\
 		btree_gin	\
 		btree_gist	\
diff --git a/contrib/bench_sort_improvements/Makefile b/contrib/bench_sort_improvements/Makefile
new file mode 100644
index 0000000000..46458ee76c
--- /dev/null
+++ b/contrib/bench_sort_improvements/Makefile
@@ -0,0 +1,20 @@
+MODULE_big = bench_sort_improvements
+
+OBJS = \
+	$(WIN32RES) \
+	bench.o
+
+EXTENSION = bench_sort_improvements
+
+DATA = bench_sort_improvements--1.0.sql
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/bench_sort_improvements
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/bench_sort_improvements/bench.c b/contrib/bench_sort_improvements/bench.c
new file mode 100644
index 0000000000..77d5c7fa37
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench.c
@@ -0,0 +1,105 @@
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "nodes/pg_list.h"
+#include <limits.h>
+#include <time.h>
+
+
+PG_MODULE_MAGIC;
+
+Datum bench_int_sort(PG_FUNCTION_ARGS);
+Datum bench_oid_sort(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(bench_oid_sort);
+PG_FUNCTION_INFO_V1(bench_int_sort);
+
+Datum
+bench_oid_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    Oid random_oid;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_oid = (Oid) (random() % (UINT_MAX - 1) + 1); 
+        list_first = lappend_oid(list_first, random_oid);
+        list_second = lappend_oid(list_second, random_oid);
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_oid_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_oid_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+Datum
+bench_int_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    int random_int;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_int = rand(); 
+        list_first = lappend_int(list_first, random_int); 
+        list_second = lappend_int(list_second, random_int); 
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_int_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_int_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+void
+_PG_init()
+{
+    srand(time(NULL));
+}
\ No newline at end of file
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
new file mode 100644
index 0000000000..97b75d368d
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
@@ -0,0 +1,3 @@
+create function bench_oid_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_oid_sort' LANGUAGE C;
+
+create function bench_int_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int_sort' LANGUAGE C;
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements.control b/contrib/bench_sort_improvements/bench_sort_improvements.control
new file mode 100644
index 0000000000..7dc05056ac
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements.control
@@ -0,0 +1,5 @@
+# fuzzystrmatch extension
+comment = 'test extension'
+default_version = '1.0'
+module_pathname = '$libdir/bench_sort_improvements'
+relocatable = true
-- 
2.34.1

v42-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchtext/x-patch; charset=US-ASCII; name=v42-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchDownload
From d82427a4c1db68c59e1d5a35f25f0391a6ae68e6 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:04:59 +0700
Subject: [PATCH v42 03/12] Enhanced Sorting Efficiency for Oid Lists

Refactored the sorting of lists containing Oids by replacing the generic function
with the custom optimized function. This change leverages a custom sort template
to enhance performance specifically for Oid types.

Details:
- Updated to use instead of.
- Updated to use instead of.
- Updated to use instead of in two locations.

This refactor aims to improve sorting efficiency for Oid lists, reducing
processing time and enhancing overall system performance.
---
 src/backend/catalog/heap.c           | 2 +-
 src/backend/catalog/pg_publication.c | 2 +-
 src/backend/utils/cache/relcache.c   | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a122bbffce..88e4ada970 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3303,7 +3303,7 @@ restart:
 	list_free(oids);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..faf70ec8c7 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -746,7 +746,7 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
 	table_close(pubrelsrel, AccessShareLock);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 35dbb87ae3..21b3f3c92f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4873,7 +4873,7 @@ RelationGetIndexList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
@@ -4965,7 +4965,7 @@ RelationGetStatExtList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-- 
2.34.1

v42-0001-Use-specialized-sort-facilities.patchtext/x-patch; charset=US-ASCII; name=v42-0001-Use-specialized-sort-facilities.patchDownload
From 530911dd2e1a60209421551bc24b5d72628050e5 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v42 01/12] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 12 ------------
 contrib/intarray/_int_gist.c |  2 +-
 contrib/intarray/_int_op.c   | 19 +++++++++----------
 contrib/intarray/_int_tool.c | 12 ------------
 src/include/port.h           |  2 ++
 src/port/qsort.c             | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..5225c9090a 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -176,16 +176,4 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
-/* sort, either ascending or descending */
-#define QSORT(a, direction) \
-	do { \
-		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
-	} while(0)
-
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index a09b7fa812..d39e40c66a 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -150,7 +150,7 @@ g_int_union(PG_FUNCTION_ARGS)
 		ptr += nel;
 	}
 
-	QSORT(res, 1);
+	sort_int32_asc(ARRPTR(res), ARRNELEMS(res));
 	res = _int_unique(res);
 	*size = VARSIZE(res);
 	PG_RETURN_POINTER(res);
diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c
index 5b164f6788..34d3aa183f 100644
--- a/contrib/intarray/_int_op.c
+++ b/contrib/intarray/_int_op.c
@@ -198,7 +198,6 @@ sort(PG_FUNCTION_ARGS)
 	text	   *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_PP(1) : NULL;
 	int32		dc = (dirstr) ? VARSIZE_ANY_EXHDR(dirstr) : 0;
 	char	   *d = (dirstr) ? VARDATA_ANY(dirstr) : NULL;
-	int			dir = -1;
 
 	CHECKARRVALID(a);
 	if (ARRNELEMS(a) < 2)
@@ -208,18 +207,18 @@ sort(PG_FUNCTION_ARGS)
 						   && (d[0] == 'A' || d[0] == 'a')
 						   && (d[1] == 'S' || d[1] == 's')
 						   && (d[2] == 'C' || d[2] == 'c')))
-		dir = 1;
+		sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	else if (dc == 4
 			 && (d[0] == 'D' || d[0] == 'd')
 			 && (d[1] == 'E' || d[1] == 'e')
 			 && (d[2] == 'S' || d[2] == 's')
 			 && (d[3] == 'C' || d[3] == 'c'))
-		dir = 0;
-	if (dir == -1)
+		sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
+	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("second parameter must be \"ASC\" or \"DESC\"")));
-	QSORT(a, dir);
+
 	PG_RETURN_POINTER(a);
 }
 
@@ -229,7 +228,7 @@ sort_asc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -239,7 +238,7 @@ sort_desc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 0);
+	sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -381,7 +380,7 @@ intset_union_elem(PG_FUNCTION_ARGS)
 
 	result = intarray_add_elem(a, PG_GETARG_INT32(1));
 	PG_FREE_IF_COPY(a, 0);
-	QSORT(result, 1);
+	sort_int32_asc(ARRPTR(result), ARRNELEMS(result));
 	PG_RETURN_POINTER(_int_unique(result));
 }
 
@@ -403,10 +402,10 @@ intset_subtract(PG_FUNCTION_ARGS)
 	CHECKARRVALID(a);
 	CHECKARRVALID(b);
 
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	a = _int_unique(a);
 	ca = ARRNELEMS(a);
-	QSORT(b, 1);
+	sort_int32_asc(ARRPTR(b), ARRNELEMS(b));
 	b = _int_unique(b);
 	cb = ARRNELEMS(b);
 	result = new_intArrayType(ca);
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..e83c6aadc6 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -393,15 +393,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
diff --git a/src/include/port.h b/src/include/port.h
index ae115d2d97..b48194cb00 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -445,6 +445,8 @@ extern bool pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32_asc(int32 *base, size_t nel);
+extern void sort_int32_desc(int32 *base, size_t nel);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 7879e6cd56..5175c8a6dd 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -20,3 +20,38 @@ pg_qsort_strcmp(const void *a, const void *b)
 {
 	return strcmp(*(const char *const *) a, *(const char *const *) b);
 }
+
+static inline int
+sort_int32_asc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return -1;
+	if (*a > *b)
+		return 1;
+	return 0;
+}
+
+#define ST_SORT sort_int32_asc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_asc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+static inline int
+sort_int32_desc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return 1;
+	if (*a > *b)
+		return -1;
+	return 0;
+}
+#define ST_SORT sort_int32_desc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_desc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
-- 
2.34.1

v42-0002-Optimized-Oid-List-Sorting-by-using-template-sor.patchtext/x-patch; charset=US-ASCII; name=v42-0002-Optimized-Oid-List-Sorting-by-using-template-sor.patchDownload
From 32f967a10a2dd6a9f68aa85717ab11ab83f03298 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sncfmgg@gmail.com>
Date: Sat, 8 Jun 2024 00:04:42 +0700
Subject: [PATCH v42 02/12] Optimized Oid List Sorting by using template
 sorting algorithm

Optimized the sorting of lists containing Oids by utilizing a custom sort template. This enhancement introduces a specialized sorting function sort_list_oids defined through macros and included from sort_template.h. The new function improves performance by directly comparing Oids and sorting the list in-place.

Changes:
- Defined ST_SORT, ST_ELEMENT_TYPE, ST_COMPARE, ST_SCOPE, and ST_DEFINE macros for sort_list_oids.
- Included sort_template.h to leverage the template-based sorting mechanism.
- Implemented list_oid_sort function to sort lists with Oid type.

This optimization is expected to provide better sorting efficiency for lists containing Oids, contributing to overall system performance improvements.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index e2615ab105..e10ce545ad 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1707,3 +1707,17 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
 
 	return pg_cmp_u32(v1, v2);
 }
+
+#define ST_SORT sort_list_oids
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_oid_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with Oid type optimization.
+ */
+void list_oid_sort(List *data){
+   sort_list_oids(list_head(data), list_length(data));
+}
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 52df93759f..88aa1ebea9 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -679,6 +679,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
+extern void list_oid_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.34.1

#5Антуан Виолин
violin.antuan@gmail.com
In reply to: Stepan Neretin (#4)
Re: Sort functions with specialized comparators

Hello all.

I have decided to explore more areas in which I can optimize and have added
two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

Hello all
We would like to see the relationship between the length of the sorted array
and the performance gain, perhaps some graphs. We also want to see to set a
"worst case" test, sorting the array in ascending order when it is initially
descending

Best, regards, Antoine Violin

postgres=#

On Mon, Jul 15, 2024 at 10:32 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

On Sat, Jun 8, 2024 at 1:50 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would introduce
similar optimizations when working with a list of integers or object
identifiers. Additionally, my patch includes an extension for
benchmarking, which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber,
Oid etc. But this sorting routines never show up in perf top or something
like that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Hello all.

I have decided to explore more areas in which I can optimize and have
added two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

postgres=#

Best regards, Stepan Neretin.

#6Stepan Neretin
sncfmgg@gmail.com
In reply to: Антуан Виолин (#5)
Re: Sort functions with specialized comparators

On Mon, Jul 15, 2024 at 12:23 PM Антуан Виолин <violin.antuan@gmail.com>
wrote:

Hello all.

I have decided to explore more areas in which I can optimize and have
added
two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

Hello all
We would like to see the relationship between the length of the sorted
array and the performance gain, perhaps some graphs. We also want to see
to set a "worst case" test, sorting the array in ascending order when it
is initially descending

Best, regards, Antoine Violin

postgres=#

On Mon, Jul 15, 2024 at 10:32 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

On Sat, Jun 8, 2024 at 1:50 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would introduce
similar optimizations when working with a list of integers or object
identifiers. Additionally, my patch includes an extension for
benchmarking, which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber,
Oid etc. But this sorting routines never show up in perf top or something
like that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Hello all.

I have decided to explore more areas in which I can optimize and have
added two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

postgres=#

Best regards, Stepan Neretin.

I run benchmark with my patches:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.609 ms
initial connection time = 24.080 ms
tps = 6214.244789 (without initial connection time)

and without:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.731 ms
initial connection time = 15.177 ms
tps = 5776.173285 (without initial connection time)

tps with my patches increase. What do you think?

Best regards, Stepan Neretin.

#7Stepan Neretin
sncfmgg@gmail.com
In reply to: Stepan Neretin (#6)
2 attachment(s)
Re: Sort functions with specialized comparators

On Mon, Jul 15, 2024 at 4:52 PM Stepan Neretin <sncfmgg@gmail.com> wrote:

On Mon, Jul 15, 2024 at 12:23 PM Антуан Виолин <violin.antuan@gmail.com>
wrote:

Hello all.

I have decided to explore more areas in which I can optimize and have
added
two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

Hello all
We would like to see the relationship between the length of the sorted
array and the performance gain, perhaps some graphs. We also want to see
to set a "worst case" test, sorting the array in ascending order when it
is initially descending

Best, regards, Antoine Violin

postgres=#

On Mon, Jul 15, 2024 at 10:32 AM Stepan Neretin <sncfmgg@gmail.com>
wrote:

On Sat, Jun 8, 2024 at 1:50 AM Stepan Neretin <sncfmgg@gmail.com> wrote:

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would introduce
similar optimizations when working with a list of integers or object
identifiers. Additionally, my patch includes an extension for
benchmarking, which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber, OffsetNumber,
Oid etc. But this sorting routines never show up in perf top or something
like that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Hello all.

I have decided to explore more areas in which I can optimize and have
added two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

postgres=#

Best regards, Stepan Neretin.

I run benchmark with my patches:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.609 ms
initial connection time = 24.080 ms
tps = 6214.244789 (without initial connection time)

and without:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.731 ms
initial connection time = 15.177 ms
tps = 5776.173285 (without initial connection time)

tps with my patches increase. What do you think?

Best regards, Stepan Neretin.

I implement reverse benchmarks:

postgres=# SELECT bench_oid_reverse_sort(1000);
bench_oid_reverse_sort

----------------------------------------------------------------------------------------------------------
Time taken by list_sort: 182557 ns, Time taken by list_oid_sort: 85864 ns,
Percentage difference: 52.97%
(1 row)

Time: 2,291 ms
postgres=# SELECT bench_oid_reverse_sort(100000);
bench_oid_reverse_sort

-------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 9064163 ns, Time taken by list_oid_sort: 4313448
ns, Percentage difference: 52.41%
(1 row)

Time: 17,146 ms
postgres=# SELECT bench_oid_reverse_sort(1000000);
bench_oid_reverse_sort

---------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 61990395 ns, Time taken by list_oid_sort:
23703380 ns, Percentage difference: 61.76%
(1 row)

postgres=# SELECT bench_int_reverse_sort(1000000);
bench_int_reverse_sort

---------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 50712416 ns, Time taken by list_int_sort:
24120417 ns, Percentage difference: 52.44%
(1 row)

Time: 89,359 ms

postgres=# SELECT bench_float8_reverse_sort(1000000);
bench_float8_reverse_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 57447775 ns, Time taken by optimized sort:
25214023 ns, Percentage difference: 56.11%
(1 row)

Time: 92,308 ms

Hello again. I want to show you the graphs of when we increase the length
vector/array sorting time (ns). What do you think about graphs?

Best regards, Stepan Neretin.

Attachments:

int_sort_bench.pngimage/png; name=int_sort_bench.pngDownload
�PNG


IHDR���(-9tEXtSoftwareMatplotlib version3.9.1, https://matplotlib.org/��!	pHYsaa�?�i�IDATx���yxT�����d2�I���]@
��JQ�K���VKYD^Q�� E����h}��h��E � ��b�E��1�B�$s�?b�Y�$3�����.�d�9s�sn`����Y�0|���$��	:&@��	��`$��	:&@��	��`$��	:&@��	��`$��	:`b�7�}����b���%Kd�Xt���k[Y�P}Qw�t��v������+--��E���9������`P�Q?W.g������u��_(t��v���3f�����z��7*�P���#G4c�t�s%s��Y��1������x����.B�������_PIP?�GNN����x-��M�>]S�L�$5i�D��e���c���vo��]'NT��u�|PW�\QZZ�F��Z�j�V�Zz��Ge��5�����6m�(((H�����>��~���2o��Q={�T�5�a�������|�a������
*$$D����t��a�%����S���U�fM����]�v�?��1�|�����EFF*$$D7�|�������y�fY,���{�6m����:������_�w�!I���~�����y��eT
��~��������^��v�BCCU�n]=��#���-�u&O����k��q��	�X,z�������;'���E��X������C


U��-�������������W�������C-]������O�b�h����7o��5k���@��������$i��1���%K�\3v�����������W_��w��K/��:u�H����[��&L����h��1C�v��?��EDDh��������>���W������m�Q�F9����j��%3f�&N��S�N��W^������g��f�{����k��Aj����O����/k����������������zJ���4x�`
<X���S���u���k�)!!A������O=��s���G����>�C=$)�KB�n������'�v��Z�t�~���h������]�9s�L��GQvv������'���_�_��W�n�Z��?�Ge��%)77W�M7���s�j���z����Y3�;�T�����^z�%>|Xm���$m��M~~~��m�&N���&I�z�*�\����k�o�^�<�����_���>ss��e�r�-����5~�x5i�D��/�}�����4g]�o�������<���@�~���x���z�)=������$�[�n%�0%�O���7$�N�*��Q�F������/^lH2`8����x�b������rrr��
�{�vn��m�!�x��w\��f��"�_�c��FTT����?:�}�������1j��B������#  �2d�K������\��(=��f���{��I�I��m���.^�h4i��h������k�al����d4m�����t9����
I��M�J,���L�����
I�3�<���S�NF���K}���C������aiii����q�w���s�o���Fdd��=_���^2$?��C����7��d�����mW�\1������P#==�0�8u��!�3RRR\��g�C��x��b�Ttq*����_������n�a�������V��t��o����m���
W�~�t��y����;+44T�6m*��III:p�����>EFF:��o�^��������}����u��g��|�&Mr�~#""t��%%$${����u��7�G��m���z��t��i9r�����G+88����_�����vy��g�2]�n��j����n�*I����d�Z5e��;wN'N�������G�{�ZDD�$����w�c�W�^���h��w�sn��l�8q�222�e���G�q��@eE�TB���.����%I111��Sv��	]�pAQQQ�[���������{�o��V���e�B�Z�n�������K%��E�.�����Z�j{�|��_t���k��Aj������?h��5��Q\�
�!_�&M�y]J��s����BIk�Z��|��={:��o��M]�tQ�.]�m��)==]_~���;yq���.u��]��U�^=�}��z���]��o��V-Z����kjB���1�@%d�Z��n����p(**J���N��7�����(8p@k�����~�O?�T�/��Q�
M �.Z�������kT�����Co������m��M={���bQ�=�m�65h�@���	zpp��n��M�6�?�����Y���_���[�n�:��]�9��������yZ�f��~�zu�����[�F�$���z�c���N�:�Q�F��=q���6m����?�={|@@����C���p�/��^�u=���j���5jTl�
��$�w0��R?{�:��wBB������\R��p�-R�
T�F
u��������S�>}��O����z��g��Oh��M����5j�����r8.���������X~R�����k�y��������3����)����W���t�R��:�u��i������o����lZ�`�K���y��*��?������O����$eggK���?�\;w�tw��%���P���w��T����*K����4i�D�]w�^z�%��vu��]R^�~��I�X�B7�|���Kn�KMM-��c���\����d��_�r)�����{_����QU���X���'�xBw�}�l6��Zlkty���[>��f��������l:q���/_��������m�������A�)>>^���s����pM�>������:{�l����������~}�����kJ��?�Q������[��aC}���Z�`�:v���������w���A�4q�DEFFj���:u��>���B�����cGY�V=��s�p��u���***���T-��~��uz�����{O���s�s�
7�F������������|��g�u�V
2D�5RJJ�^}�U5l��9��<��_]��w�������k������4o�<��Y���i���"""��k��f���Q��n��&����!A|�k���9s�^{�5�Y�F�C�N���Iz�����sg������_�*5n�X��s���xq����5k������SO=%�����{�����f����MAAAz����i�&�t�MZ�n��r�2�s�=��?��W_}Uiii����]w�����;�z��i��z����`�eee�}���������$EGG���^����u���+77W�6m"A�j�2����N~�^p����k����.I���ot��i���[:��������{k���I�����y�f=���Z�t������eK-^�X��w�[�e���t�RM�:U�����������������`:&@��	��`$��	:&@��	�z1���=��5k�b���8�*�0]�xQ
4�������P*��&A/���g��b��3g��a���.F�D}
�H���M����3GS�N�C=�y����3C���5k�j�*�v�m�}���;v�6m����P�=Z�g������[�fMIyaaae����u�������le>Ou@��G��G��G����8���+&&�Y���<U_K�(
b���>b�>b���Xg�6A��g�^�u�o�������+�+[nn�����h���CIII5j�l6��}�Y�������r'�!!!
�?�5+�+�+�+�x#Nt�.;O���J�X��8��X��X��2���������#G��7�P�Z�
�?p��^x����[���[�NG���o���;j��A�9s�.\�+W�TD�(5S���7NC�Q��}�����e_ff�~���k������.���;w�]�v�W��s��4v�X>|X�:u*��������v�NOO�����n���^��[�sT��}��}��}��=����.A����o�>������?���u��a���?99�%9��|���\�ug���3f��n�:����[�b%$$����r�r�r�r�'����������T	��3g��C)!!AAAA����G��q������kO�:U�'Ov������������~��1F��������k�rss���#�0�~ONN�v���n���jR����8Y,����j�{L~o-@������Sv�]������Rnn���[�&V6���:�������{����n���-77W[�n�+����c��������py��#��gOm��Y�������]��;wN����/00P������l6�|���y�b�>b���+�0�������2�7::ZIIILZV���)""B���E[�M@uU�:�Z������3g������*���(�J�������l3f�Z�j��{Lu����>���]�vz���4t�PIR||�f�����EEEI������������/QQQ
		)U�p8������P���rQSp7N�a(33S)))�����WT&P�:�$�����u����5k�m��.�j�����k;��
�&M�H�������8�{��z�������i��i��qE��@U�����"P�v�R���p���+


��/Ai�,I���f�:�����%��v_e�������j�'�|"�����x�s�=5j��y�_
�*|�'&�����}0c;T�����lS��e���%�/j�F�i���^*���������*3�}U�t*#t��m��Y���Yp�,YRh�����]�+��\i���n�N5jX��_I���rs��[��$�~}�gO>��s�-��c���7o����2J������%��q2���'�w�q_'��b�c}YD�P����9��MX�
.U��d�M�<��K�y	���\)=����w�������
������}]:(������Z��w�l����+W�������j�JKe�d�����_���t|�q�t������:6���j\q��*�$�tq���+���V._p%������\��r@IV��F�������lM�8QQQQ


R�=�g����d����*$$D��~�~�����r��������+((H�5������5l�0���*,,Lw�y���;��?}�tu��Q�����f��)::Zc����-[4�|Y,Y,�>}�\�@�:�y��/�����Ul+x�;�M���^�l���[4q�D=������Ttt��O��rLZZ����?�n��
�����/��R�t��Y�V}����Z�###u��7;����o+&&��2�X�B���Spp�j����}����K��=��3j�����cG�Y������O�b��_���z�����-_�\���.\������'3���rs�Z���\^�!Y,��I��at�]�!ef�wl�g����.�L�?�zH�������������G�|��K��Q�Fz���5`�}������,t����u���k��������f�=���%^���_�G}���_���:s����9#)���O��l�����7Nw�u��J"_��>���X�B�/_V�6mt��	�m����g��u��i@�b�2��U�����>���K�<.�R����.]���'k�����s����>u��]����$�q�
���~���p�����������+EFF�c����y��t����b�h������p���{�.��III���~���^��~�.^��m��9W��?�^x����������z�-��7�������E�y�q�������.]���^zIO?���?.I


u;����m+�r^�aHg��w�-V,�Pf�T������0�>����;SF�T�����t��-Z�%K�h��A��7�xC			z��75e��B��?��G}T�t���k��.O������-Z�G��X,j���s��
t��A�:u��D�������i�={��k����Z�����v��JOOWXX����h�S�,Z�H�-r�h����z�)��c>�04x�`�Y�F�V��m���������c�j��M


����5{�l��o�*2��
�������^n�15C5�����}{���-Z��W^��
��_?m��]���RRR(I�;w�>��C�X�B<��n��m��Y�<��6o��~�����c��}����7;���%%%)''G��w�����s��;w�{�1�}������{N�6m��y��p�B�q�&M�����p8�u��b��5]�=,)���@Uv��I��vu�����f���o���G�|���Gu�M7�l���/�:��w�8��-[j���Z�n���bbb\�����)""���5�����
j��9��w�����z��6l�>�r��y��l������!Ct�����CK�.��%K��SOU�-���}{�����WJJ�$��/�TFF�j�����P��S�N��������{k��������-[t�-�8���g������-��Tv��A}��Q�v�t�w��7��O?�$IJOO���g]�3HR���}g����'B�<�����={�UHH^K�;�n���q�WK��x`��u+�
7��S�N��O?����u��w�o��Z�b�����N��jd���.�g���E�i��]j�&of���^�_|��WU|�����#G�~�z��WO;v���3��c�i���
��{o��(c�{��������5��:z�nhp�[�.
�����b���pH�222T�~}�!`���;����.^��}��i���z��g�9s��C�j���Kw���V��c��[�N,�O<���w�v��n�Ce��I�=�g�������q�K���=+�l����n����������gW��������=;F�f���>�����n�k��=�4iR��i���v����m��]��VXX����.�u�]��o��*55U�[�v�I�oE?r�����W�9������Vm���Z�|�.]������������Z�pa�]
w���v���^�z�m���cu��au����kegg+;;��:==]R�����������q�m`o��q)qr_U���n�ar8��6�?���Z�:.�?��s������r_�~����;*99Y~~~j��q��:����}��Z�`�l6����z��SG������^�z���(��������i���I�\�R?��4h�����g�d���>S��]]b��{�}�����n�{�v��W}����$�f��-G����}�-�!����yL�\�V����y�E����{���F�;v��L����H�������Wff�����"�3q�Du��]s����a��v����K��/������S�N�������������W������#5o�<����/��z��}�.r�7����u��i���*22R~~�g�������,���j��U��?���u��a������d��\��urrr���={�f��Qh��u�R��'3O�u���������$$$���qr_U�����������Q��9�g,����L�CJO�����+W\����2==]7�x��v��a��i��j�������n�:����v>\����?����7�Qzz����u������������b���_h��-���[U�N���W?���bcc�������k����_����k�w�yG��E��������^�t��u��UFF�>��c�m�V���E�W�\�����u��Bq3��y��H��`�pi���u����a�\�`F��KK�f��
����9s���p��{������K�]�V�j�*���o�Yo����~�i=��S�����M���3g{��5k������'d�Z��kW�^���L������	��W/���i���Z�`�5���#�h����������u���"[���-[����p��V�X���Gk��-�����q�F�������:u�&O��|���������_aaa�:W��D=q�	e���Q�5H��
c����v%$$�_�~������}U-VYYY:s��BCCT��62)�T�gS�5P�ubK��w-���
p9����l6�s��5k4m�4M�0A?��������gO5m��yL��}�h�"�������[o��C�4p��b�]�~}}���z�������F�i���1b�$i��)����SO=��������?t>����F�
�a�x������|P���~��G=��SE�������`����������b���C5������p]�p����ss�����3?=�P�^x�J�y	�v�V�^���W�fo"V��N������S���I�R�����F�0}�����������^?�������^<Q��I��}��Y3���_v�Onn������gOm��YO=��>��#8p�y��S���iS�����.�W+o/$�|�y�������n��������3��	�Cr^����Z��}U-V����?���p8��6
�T�^[eQ�lZ���j�����������r�ZYe�p8����3f������v�����^rN.�Y�f)%%EQQQ�������]s��'���*6<V�~y_��-����J$*������'���H�+�U���S5h� �������Z�l�6o���k�*::����bcc��IIR����{��W�?�����5m�4�7����/���	����+�^JJ�F����$����}��Z�v��������V�>���;V����Q��F��g�y��%/�ET��#A����o������i���V�^��"P�1�&@�^A���8_��Q�0�'QT.f������1@E	������=��u�*  @�R|9]�rEYYY,�Rw�d��\��~�A~~~
��R��J����urI���W�lt�"�����I%%%�����~�a�|�����IVJP�8���(66�/QP���N.	���*c�M�UH@@�bcc�������R��n�k������k@��4q�Z�����TC���KB}���Xg��W���(�E6������jUNN������K@��*k�\�!�U�X����h4��X`$��	:&@�^A���T�"A�2��`~����S A�H�0�
��3��!*l����{s�`~Qa|� A�H�+c�%!A�2��`~*l�	��`$��	za:��:�_"A�2��`~��0tL� A� �A�����������$q_"A�H�0tL���0I�gPa|����$���
�{$��	:&@�^A�(		��1��PaL� A�H�+c�0?CT��!A�2��`~��0tL� A� �A�����������$q_"A�H�0�
�t@IH�0t/c�8��"*l����`$��	za�8��u��D��e�A��,T� A�H�0�
�t�����	��1�
�ct���`$���-Z����+,,Laaa������~*IJMM��	��eK+66V'N��\�����!C�($$DQQQ�2e�rrr|q;����P]0�
P�4l�Ps��Q�-d��.]�a��i���2Cg�����s�o��V���u��Y�X�B�����!C�(::Z;v�PRR�F�%���g�}��w�o��}��9�X,�4i����D�1���h���<x�Z�h����^�f�Rhh�v����m���>���C��Y3�z���5k�>��cg}�n�:9rDo���:v��A�i���Z�p��\�R���:�30m��={�����}���mg����<&����������K���/��.(,,L��y_=v���v���^�z�c��c��������S�����Vvv��uzz�$�n��n���
��<��.�cD�JF��G��G����XUT�M��gddh���z��7������=��|�f��i��Y���{���#������^�z����f����{L��OW@@�/n	�*�������WVV�BCC�j�*���:�����9s�x�����d��\��urrr���={�f��Qh��u�R�[�W~p����P��T7��=��}��}��}��Uff�Jrm�L����!C��o��.	zQ��D���H�SNN��vG��S�4�}��}��}��=��i���l�R���b�
�=Z[�lqI����5d����i��������S5y�d��������
+�y/$JG�~����l6[y�Z���v%$$�k N�#V�#V��d���Co3]���{�i��}��g�5��O����(�����k�^}����Nx�>b�>b�>b����4�[��ysIR����g���?_����$����8p�j���U�V�|9�������r�s��9�'00P������l�r}�
����3d��\�	�rqr�r�r�'bUQ�6U�~��=��CJHHPPPP��V�'�+W�M:��ys
������x�>b�>b�>b����4��8go���t
0@�������
������5k�RRR%)��GXXX����L�����W)))�����rss�u�V���+�����j�TO������[�V�l�2��:�i����������{*��xo�:u�
���X]�xQ��-�����v�Z�����������o����t����u��j��������������^����6m���Wd}@u`��O�>:x����1c��U�Vz���d�Zy"�	���h��QJJJRxx���o��k��_�~��y�v��-I�.��N�:����j���O>���c�5jh���z��g|q;�����5k�m��.�j�����k�m������a���x��o�Y��[n�E�_�F��z�jO��Qa|�T	�������=��X|rYP
*l�	�>A��y�����D��������0Pt/cH�g6��H�0tL���0Pt/c:����;���-$��	:&@�^A�1(		��Y,d��]�:�����C��	��`$��	za�8@IH���b�u	��X��&@��	��`$��1����|������YD�
�=tL� A� �A�����������$q_"A�H�0�
�t@IH���1������ A�H�0�
�$qT�6�GH���I�0?6�H�0tL���0�
����6�7H���!m��ET��#A�H�0�
�t@IH���1��_�u�
��|� A�H�+��%!A�2��`~�A7�Su�o��`$��	za:�$$��:�WH���I�0?6�H�0tL���0�
���u��B��ei��,���	:&@��	��W��JB��e�ATE�-R������0�����O?u������q�T�vm���j��:w���95d����(**JS�LQNNNE��$�u�
��|��Z��
5g����W_|��n��V
6L��$=��������|�rm��Eg�������������!Ct�����CK�.��%K��SO����9t���M���,�=--MM�6-�������E�����5k�,���j��]�p���|�M�������[��sg-^�X;v���]�$I�����#G���o�c��4h�f�������+�_*_������N�>����B��������������Z�|�.]����x���Wv�]}��u��U+���j��������s�N�k�N���s3`��;V�V�N���Vvv���������%Iv�]v����P��v�]v����:��Wyb^'�+�+�y2Vot/c:��>��#��k��Uxx��unn�6l�������T||�����U�V)..NP@@�"""\��W�����%I���.�y���}��={�f��Qh��u�R�{�w9������+�/����N|]�J�8��X��X�������@I���*���ns�>z�h�}6�M�7�/�P���l�R���b�
�=Z[�l)oqK4u�TM�<��:==]111��������|�����y����G5�k���U��nWBB����'������qr�r�r�'c��c��H����$�I�&���/T�vm��7  @��7�$u��Y{������u�]w���+JKKsiE?w�����%I�������]��?�{�1E	T```��6��\_�l�_�[�sU'��=��}��}��}��UE��I�*c��d����iS���z��C�������l6�6l���w��q%&&*>>^���*%%�yLBB������2`f��{c�f`�������c��:u�
���X]�xQ��-�����c����~M�<Y���
��	��o�Y��������{����?���dM�6M���+����X`��PM�s�=z��7=r����5J-[�T�>}�g��]�V����$���K����#F�W�^������+���Z����Od�Z�{��G�F��3�<���P��@5������zK���W���U�F
��/������V���j������Q#�^���kP���PM:tH7�p�$����r�gaL>G�^A���M�6����A�2$f��w������u1L�������� A��p8z��g�F��Q�F������3�k����;��O<�7�|Ss��Q���%I��o��������Y�f���To$�TK�.������7���s[���u�u��/�	:>Fw����T�j����V�Z)55�%2������
�t��:��W^)���W^Q�|P"P]��&��y
2D���W||�$i���:s��V�^���S����3G�E�&Mrn�����q�T�vm���j��:w����5d����(**JS�LQNNN�������wo}��W����������4
>\��W��=}]<�=������G������o��������-_�\���?~����>�L�����!C�(::Z;v�PRR�F�%���g�}����u�f��A&�+�����-�9r��x�
��U�����z��7���/��[oU����x�b���C�v��$�[�NG���o���;j��A�9s�.\�+W������5k�h�����.T������^?���K$�&������!C��o_��{����nw���U+���j��������k�N���s3`��������s���)S���.I:x��&O�������S�<y��KL��������}��g��B������������Srr�����y���}����Vvv��u���.��^�{�$�C����q�nw��<�A~�����X��X��X���q��X�:uJqqq��>�@C����>�}��i���ZP���3g����RBB����*���g���3
m_�n�BBB�|�o����B�~��V���			�.B�A��G��G����8effz�$�p^s���5j�$)22��`��bt��*A��w�RRRt�
78����j���z��W�v�Z]�rEiii.�����Stt�$)::Z����y�gy�?�(S�Nu�����������_aaae���[�~6j�H�7*�y���������O6����15b�>b�>b�O�����=zh�������>��s��_��$}��Wj��a��f��O�>:x����1c��U�Vz���#���
6h�������+11���k||�f�����EEEI�k�sv�+J``�m��l�����+I�Z�d�Y�|����1�N��������{<����+������/Z�b�-Z����N�����j���ZP����5k�m��.�j�����k;�����<y�"##�	&(>>^7�|�$�������������^����6m���Wd@u�O>�����^z��W3U����^zI~~~1b����5`�������V�U�|�������x��QC�G��3�<��RKg���	����]^i���Z�pa��i���V�^�������3��,*0I�x��
���~��IM�6M�������")oLk�pmI��l��v��i���Z�r�222$I_~���~�iO\�*�#	���?����oJHHP@@�s�����]�vy��c�%����j��e��GEE������D��t�/
>��cW�\������@�m�T�#iA���PRRR�����w��
*^xx��OXX�6l��/���������a����}XJ y������c�=�����b���p���>�#�<�Q�Fy��/^�������w���^{MV�U��������/
�U��<�������U�V���QFF������W/u��M��M��%*=z�|�����#�<�L�%�j�j���z���|X2 y�=  @o����|�I:tH����Z�h���Wj�A�ENN��;��-[�l?v����Je��0�$��bcc��S3f����~�<yR7�x�$i����3g�������$��ah����i�RRR
=�����`s��Utt�^x���������)S�?��?>.�H�>i�$�������~�z���,U�<�A�����}�Q=���JOO�$&��D<�������V�\���{�tU
�*fDb��u�f��=<<\M�6����u�����m����ri@I<��O�>]3f��[o����`O�x�m����"7y$A���;����***J�7��fs��y|�����u��<���=Z{���=���$q�`8�,�����G�J���i�N�:��D@�P�����Gk��U�=<q�*�g�HII��w����7+""B�����_��Wz���T�n]���,*0I�x��
?O�$&&��`0�	&����:|��RSS����C�)==]'N�u���<�����z��Gu��iO�x��5k�����u���mqqqZ�p�>��S�H��~�=�(33S��5SHHH�I�RSS=q�J�1�_s8��hI��lr8>((�#	��y�<q�*�1�����[��C��w�U�
$I���~�a����������<U���fq���+��7���7n���I��3g��m[����>.(s������.==��c�@������}��~�z;vL���uk�����%R9�Z�j)))IQQQ���(r�s�0d�X���[�BV�����bQ�~���_?_\��	���)IZ�x�bbbd�Z]�q8JLL,_	+9��`~��0�2'��{�v���?����^��?���}�2F�k��:��]�������� O\�*�\��O�<YR�x�'�|R!!!�}�����{�:v�X�V�A��\	����%���<xP�}����y������c:�LN�<��������?��������*66Vm���u�|�u�fP�}��M��1c�h���,���m��E�
R����u�V��5KQQQ���/���oj���."��G��/^����{�������M			.��n��V�����%��t`~����^h{TT���?����H�+�������B���������%��{��������c�=���dY,9}��gz��G4j�(_�4�T�$�T�>��Z�j���edd(..N�z�R�n�4m�4_�j�\����#  @o����|�I:tH����Z�h�����W���"66V����.�
	��1`�ah����i�RRR�p8\��\��G%3�,2d���:�GH��&&M���_]����T�^=Yx���0I������i������O�d�-^���Oi��=[]�vU��5��n�M��w9&99Y��{����U�F
�p�
���\�IMM���#���������(��P��Wz�|-<<\M�6����l��q��i��]JHH��nW���u��%�1�F�������G����>|����N����y���#u��a%$$��O>���[��x��T6$�^F�A�YL�>]3f������}�5k�����S�6m��C-Y�D�����w���;vh��	�����iSM�6M�c�=�5k����u�M7�G�Z�`��{�=�={��e,��.������PM�y��z��w����f�����o_��}��IRdd�s[�n����KC�QDD���}eee��[n�$���S�����=}������v����o���u�������|���.I������e.A�<WU��T2��>b�>b�>O����M�@51z�h���W��s�G'�s8�4i��w���m�:���������T�vm���+$$D�V�R���%��Q���r9����"##���\��f���3f��n�:�����F~n8��u�j�j��\�DBB���P)'�+�+�y"V���(����W��|�?�����]�=zx�������C��}�v��O>������~�z��SG~�����Nm��M���+���N����';_���+&&F���WXXX��C_J2�^�z)�VL��U���v%$$�_�~�zb���}��}��}��U~�-o#A�2���"&&��I�U������a����'O��+���C��M�6��:h��mZ�p�^{�5EGG+%%��|999JMMUttt��T```��6���_�,����m�|�u�'�^'�+�+�y"Vk&���x�����������>�a?~�V�Z��7�I�&.������~��Z�r8����x����L,�q�F9�t�M�.#�
-�T��s�233��Y3���j
HMMu�\�����e����[5k�t�Wpp�Z�j���������sU�vm}������$�u��8p����?���^��n����u��w�A���q*	�
b�u���y�<v�E�I�sF�|�/�}��'�����W������C�������k���<x���w�yG���W�>}����#F���_�X9�LH��&F���sn�~��E}��%�e��y�XTj$�^�$q_JOOwNw�h==�\ec�X$���x	:UX�Z�������(EDD���a�X,����A	@>t���7*22R��i�&������[��{�v���I���jE7Cg����������1`M�4�?�Ph{jjj�u��#��*mC<U�	:�D�X��edd(((�%���*n�����f)��'��������w�c��>*�G�^A������K�kA?x������C=��#�*�	��1�k����3F�������'��?��|��jb����.(��`$���r����{c��X�k$��	:&@�^A�(		��1���B�
�1t���|�7��J[�X�u�u�����\_�T%$��a�J�qc�_?��b��������x	z��T^+WJ�����w����>o;I:�t/c���rs��*�Ak��I�Dw�*���J�O��A�@	�n-�r^�aHg�H��U\�@����`v�t��t��t��/?t��II�-��H�+c��������?����_K99e?o���+#��H���1�P�C:{�5�������*�j���u���-ZH������������������s��:�_!ATZ���vI�O�/^,�}���&��?�����/��7[������;o�d�z��@5D�0����&�'O�-�j��7/�"���^��.�X�7�{�	�6�K��/��8�.A_�h�-Z���OK���i���zJ�
�$%''k��)JHH�����eK=��1b������0a�>��c���i���?�BCC}qK���RT�����tK/���n-5k&x�|��K��I�6���Oh�������i9c��a���3g�Z�h!�0�t�R
6L���W�6m4j�(������>R�:u�l�2�y�����/��S'I���#��������v�3F<���-[V���tpe��M�vu��;�����^R�to�Z���
]���z��@r^�������.A:t���Y�fi��E��k���i�;vh��E���%I��M�K/���{��S�N:z����Y�={��K�.��h����;w�4hP���������'is�[z�$�,����2D�7�o�.A/(77W��/��K�/I�������i��!����������,�r�-���;w*""���KR��}������w���o/�Z������v�NOO�$��v���r��!�*��!�=������\�xW��}��}e��/��-:~��c�����~?{�����PC-[j�J?��{]R�t��z���.@A�L�<���xeee)44T�V�R\\�$�����]w����k���_!!!Z�j��7o.)o�zTT����������b�9{�l��1���u��)$$���r�D3Imu�l�V��W��T'			�.B�A��G��WT��v���k���k���P}�}M}�]���������Jj��R����a������B��O���SYx��Tff�J�
S&�-[���t���X�B�G���-[�'�|RiiiZ�~�����?�Pw�y��m��v�����S�N�������������+,,���=r$��\���5x��2��:���JHHP�~�d��|]S#V�#V�������M���>��k��[��Z���F��)�E�j�k�.����t�������"~�S5x��T~o-����c>f�=  ��"��sg���G������>�W^yE�R�6m$I:t��m��p�B���k���VJJ���rrr�������b�����B�m6[���Y�y�����d����<�Iyc^�+��_����E-[����!��/4��L��ZI��Y~��^��O�����9,��S&�Ws8���vv��sMt���1�����4���W�;w�$m��Q�C7�tS�|����	������1)#��w�%�
j��R(o���)��t	���S5h� �������Z�l�6o���k��U�Vj���|�A��;W�k���~���}��'����[k���������^{Mv�]�����w������^������b����-���e��������������������5JIII
W����v�Z���O��z�j=���:t�222��ys-]��e|�;�������O�>�����#���/���$�u)��*�-��.�y?���_������n-5mZx�t���?����t	��o�Y��-Z��>(����H-[����*3��(�+W�Z��j/�[z�
'��Z�-(
���<X�+�K��:���~����'�������-���V��r,6� A/���~u�tw��_=I[Q��Pu��W��UW�n�������-��e���P=��{_�����n��?���^0�[:`>��+m�A�
	:`�w�����Q)9���]�-=�g�f+��$����n�WO�v��t�R�������-�_�3(�
Bo9�7�����k��y�����d����[��Z��[:����hQ��`�����''�$�*�}5k=I����)t|�@�q��t�D�.����^��eu���8?�d�n��b�no� A`:ii���G�o��v���'ik������i������*�>�� A� �A\�wK/j��k��~u�����i����v���I$�^F�YTw��L���-��d�n�������H�x�����^�[z��'ik�*��|����GH���0�3g
wI?zT:w����wK�z������	:�B�����.<>���kwK/j�2���F�^A�-3JK+�%�4��&�tK����h5��9y��5>��n�aaEO�F�tU�Ey�6��|��"������Z��6,�%�uk):�L@E"A*��~��h��Xm���'���o��k-/J~������lI�t�,H�+c�Q����/�S������N�t�r A�2��$������-;vL��,�}
�]���Q[qqV���p�g����+u��1�[�nz�����eK��v���'�xB�w���jU���v�ZK�RSS5a�}��������#4�|���V�=Y~�et�������
'�������eK)((G�W�����e�Y+�f@��-[4n�8u��U999��_��������#�Q�����|����:u�,X }������s�g���JJJRBB��v����x@��-����3$����wK/j��k��^�n�v�w���f���K�,QTT�����^�zI�~�aM�8Q�?�����-�G���5k�g�u��E��`�
<Xs��U�
*�N0�
Bo���`����������^��etKP\�pA�)IJII����5r�Hu��M'O�T�V�4k�,���CR^{DD�39���}����O�w�����^�:������v�NOO�$��v�������:''�������C�JF��G��G����XUT�I����������]��+�[����-��e��-@U�p84i�$u��]m���$}��7�����k�����������O�>:t��Z�h���dEEE�����_���JNN.�Z�g���3
m_�n�BBB�u�_�v������r���HHH�u*��>b�>b�>O�*���8"AG��pHg�N���RR�_~����-k����T?�����C��}�v�6��O2|�A�3F���S'm��Ao���f��]�kM�:U�'Ov�NOOWLL��������r��p<@�,�w�W��v�:WUg������~���F�W,��>b�>b�>O�*�������Z���~u��;���N�t�3~�x}��'��u�6l��^�~}IR\\����[�Vbb^�ttt�R�z������TEGGy���@�n��<�E����/�n�d��2��>b�>b�>O���bM�^A�^1
vK/����-�`2~��tK����	&h��U��y��4i���q��j����?��������A�$I���JKK���{��sgI����p8t�M7U��`"$�^F+����~u2^�n��?���7n�8-[�L����U�fM�����p�b�h��)z�����Cu��QK�.��c��b�
Iy������'���k���?~����n���n�6��H�aZYY���^0	/K�����z�x`��h�"I�-����}�������$I�&MRVV�~�a����C�JHHP�f������;?~����#???�1B/��rE�F���|�>����x:d���������q��)���Y���>���.��_-22R��-�T���H�+Hu��-�����tK�����}���gJo�Jj�T��_/�*������m��������=%��3�.�-�`2~��t�r�����Z�t((��h�6m�tKP���{�����+�������m
J��K�����n�W����-����-[J��������i��X�lzb��,?W���y�7����W#+WJ��m������}�
�$���O�v�����_�`���?�4�[:�t�����k9/j,�a���?�����J_}�~������-J����z��m�\��_�0��f�p�n����~u��-�$�^f����$����[<�n�����������:��"��&BB�;n�t��[�Y@Q�|]x�����X�1K����=+�LW$�������fg��N����\*��>���y�[P:$�^��1��!��������yc��|R:q"��u����a�%��n,f�8Pm1����{���>�$�u�N�[oI;��>\6,oV��$�~��n�������|���0��D|�d)=]
��zJz�����
�Z�����xs���J�����������6m�wM�g1���9����^}Uj�6/9
���w���H�(��u��Z�+����[:m����=zHo�)]�o�@e�t����^���J/�$�o�����H/���;�9T^��WO�A?vL����;�^�z���R���?7��hA�rr����[*m�N�fM������I������,��5�<(�#����z�@���bb<P8�d�yfW��K�PZ�M����g�������i�i�j�s��hA7�}��Z��������I�I����\������[.+Kz�	��������w��V�"9���t/�y8�5���7C���y���SZ�@���^��/,�y:��|����J��IIIy-�={J���SO��m�pH��I��*
���*	zIJ�7�����mu�JV�������{�y���H_��K$���/
�u��������?�)
R���	���39\pp����w��A?v���Vk���	������,�!A��'�[���-�������rK�	!��#I��Q����������e��,=������[P2����1Y9Y:�y�J��H��,,���X��7��{����B7TT7w�Ej�0o�5�;����������+t/�+����s�h��������\�S�;�k�������AW�������P>$���}��E?�����
�����}V,PJ�F��%����}z}����[�n��nlp��^�U]tU�ZM�3���
-=�`��N��_���N�9��'��D��L�9�G{�����{u��EmO�������EG�K�..I{����t&�Bb����T]$�^V��yZZ��v�XR
�q���~�����
�����f?~��>��sg�~ ��R/�j��uZwr��������-��u]��AEE8�'^HT�WZ*+'�����T]$�����~����r���,�^�&�?HuB�z_�����nk��8Z�t%���{����}^�������G�������*�:�����k_���7������s����H���!A�@��y?##}[P��_���%C�hr�d�f�j��ejY���w��X��Aui�Ec5V��q%C���i��{�������=:�vJ_������+�s����� A�@$��_�:-�~%]��#��b�c<r���P�j�K��rn;�y^_������H�����k�k��U#JA��.�����x	z��;�w1��r9���!��z�:!u4��@
l>P����^uy��5��C��!��2]���F����������{Y��fhA��,������&j��Bl![7'�{������������W.J�.�/�������}��bQD`��9V=w�9�
��������+�P��@u@�^�H�0��=2��v���uC�J��G�����N��K�3����gJ��|w��e�������n�'�P���W��.�$��W~�^;����+����"�#����`�>���nY�V[)�����g���g����O��^<[�r��z�=	>�������Rz^��tL,�r^�7o�?/J��:
���:�W/�V
&�1�1J����-�fsoLz�#���k$��s.�r�e%e$��>J��5?�?�L�w��{Y�P���sI���IQ@	��;t���������:�y��c�]��l�~V�
��Z�ek�(*�/���$�?��r��~�-T_]�J�~l���uI�\	zIK�����%��i���{lxl�L���	��+�<??����RrF��3�����'u�^���1�A�Ae����R�E�i��E:}��$�M�6z���4h� �1;w��O<���w�j��c��Z�v����%I����0a�>��c���i���?�BCC}qK?�)�'��07g���.��������EQ	���"Z�t����OJ�)IW���B�2���h
,�||��L��7l�Ps��Q�-d��.]�a��i���j���v����j���Z�`�������_����y��#G*))I			���3f�x�-[��g��qT�nA�9�%����Z�z�,��U��h�/f~~�����s�����se���&�W���r{9@�g�}���.�g���E�i��]j���~�aM�8Q�?�����-[:?z����Y�={��K�.��h����;w�4hP17r��� #A����A��$q���,~

WxP��Q���0��4c���D�`
p��~q-�$�@�L������������K���WJJ�v����#G�[�n:y��Z�j�Y�f�G�����GDD8�sI��������{�n�~����Itq��~�����t�I��,�CW2����?p]����K)J��R�r��������up�A��]�8�`�`|Ti�L�<���xeee)44T�V�R\\�v��%I�>}������;���������C��E�JNNVTT������������dgg+;;��:��5��v��v{��%7���a������(�����8�'���r�r�r�'�D����!�tT-~?��),0�L�L��{�F7}w�U)�J,���V�1�$�0;S&�-[���t���X�B�G���-[�p�%�>����#I����6l����zK�g�.�5g���3f��n�:��������H���:5��V�>Z��U			�.B�A��G��G����8effz�$�Z��_L�U�F|�0Jl��1�G�?�_�
"�~%���������C�e���&�W��������L���y������;k��=�?�s�y\\����[�Vbb�$)::Z))�O�rrr�������b�9u�TM�<��:==]111��������|/))���]�6���M�|���n�+!!A�����f�uqL�X��X��X���q���s��K7-���X,�����5Sh��n����	���l-���u~��[�����\c�I�q-�L���p8�������A�:~���������[||�����w�^u��Y��q�F9�t�M�^#00P������l�r}�Zs\^��c��f-�������:!V�#V�#V��D���yYdQ� &���%�W��Z��%��2���O�s9:�y^�3���>J��_=���F�����t	���S5h� �������Z�l�6o���k��b�h��)z�����Cu��QK�.��c��b�
Iy������'���k���?~����n���^��`~A���@�*,�BB��a
K���	~Y���8r���[-y���rmj��@��k���~
[
Z�M�t	zJJ�F����$����}��Z�v����'I�4i�������+55U:tPBB��5k�<�;�������O�>�����#���/���\��`~toP�'�L{�{��%$��F�~���$�8vq�����
5}��x!��$''G'3Oj�~�����uB��i���b���7���1�?���:�W�����e�<Y,�a�5��	�x��bQ���PC��]W��L��g����k��Ske���t�OY?�$��I~iY-�B��K3��	~��D�|���r�\w|���A�A:>��i�t�%�U����hA��,������L����t��i
j>���L�������ow��k�*�r�R/���>�,~��_3�f�	��������d�d�|�yt�!A��hAP�X,��BbQ������0e�d����O���a��p�;����}�?����|)	z
���}]
�o���Z�r��;���`u��M�=��Z�lY�X�04x�`�Y�F�V��m���������c�j��M


����5{�l�XA_!A�_X,��lV���K���&�iYi��{E����~�OY?y�.���EF��@e�e��7N]�vUNN��������:r��j�p]h��yEvI�����!C�;v())I�F���f���>[Q�R��`�����7���V�"��V]���W,t/+�}�����b��5.��,Y���(���W�z�rn?p��^x�}���_��K��u�t���_�^���S��5s�L=��c�>}�*�^�|��t0� � E�F+:4��c�%�#A����PU]�pAR�J*�233����^.Ttt�/T;w�T�v�T�^=��h���:|��:u�T�=������v�NOO�$��v���r��a�����}��,?6��d��}��}��x999nW��UT�I�+-�����ph��I������m������[�n6lX��KNNvI�%9_������5c��B���[���������
�N<���W��|�ABB���P)'�+���Nf�t�����+)$�T�.XGx	z"ATE�����C��}�v���>�H7n����=z��S�j�����������Q���V�s������>��@u�.���<v�]			������<UG��}��}��x����'��[�RkA� 
�7��������6t/c:�*?~�>��m��U
6tn��q�N�<�����G���={j��������������s�Td�xI
T```��6���_T�,~��������O��: N�#V�#V�5��L�'��������o��]=z�p�R'�N��@��X��W ���
�04a��Z�J�7oV�&M\�?���������]�vz���4t�PIR||�f�����EEEI����������b��TN�����n�+)$I��;U��$��t@U1n�8-[�L����U�fM�����p+::��V���Xg2�������{����?���dM�6M���+����Zb+�xv������SH�U��E�t���r�-�_�������/��a�Z��'��j�*>>^��s�F��g�y��%������\�o���
��tqT�$+�{5j���M2[��uvd0O��Az��c�9�WH�+	:�G:�W���e�/�>,5i"Y��+�E��D�r<Y9y����}I�$�}9����V��&O�%��6�aCi�|i�p���%�-_i�L��}������%IA�A:>�8I:�B���KV��~�[���\��}���+}S.��|��B����r��-�x	���J=$�MV��LK���&��D����I�}W�~�����;��+��<{��#A����={��#A���=�fk�X��o�H11y� ��{������$Y,�����}�<�C���K��V��4p���a�v�A�����Q�P����NH�
*����u�������s4w�n5jt�bb���'-��Alx���?�\�<''G��oW�=�����NH�������j����V�]�5x�!�������:p�����$u��$�6����	��`$��	:&@��	��`$��	:&@��	��`$�����`V�aH�����u�����L����f�y�hU�r�r�r�r�'��_���9(=O���J�X��8��X��X��2��$���x��$)&&��%Tu/^Txx���Q)Q_*���l��c�"9�={V5k���b)�y����3g�(,,��%�z�����������{<'�0t��E5h�@~~�:+O���J�X��8��X��X��2�������O
6��������&b�>b�>b�>b�O������t}-��4��{���������*S���zL� A����@=���
�uQL�X��X��X��X��8U]����X��8��X��X��2��I�0Z�0tL� A����q��


�M7���?���E����g�k���Y�����t�m�����.�deei��q�]��BCC5b��;w�����D
2D!!!�����)S����r����u�
7(00P��7��%K
��2�z��9�X,�4i�s������{��G�k�Vpp���k�/�����0=��S�_�������o_�8q�����9r��������_.�����U��=���=������|�r�j�JAAAj���V�^���.���\=���j��������Y3��9S������u���
�b���?t�o���Sx��?��:�l��KF}����Q_������{F@@���[o�6���?��s�|]4�0`��x�b���C��������FFF���?���FLL��a���/�0n��f�[�n��999F��m��}����7V�^m��S��:u���o���		1&O�l9r�X�`�a�Z�5k�8��L�����������7z�!�vb�'55�h���q�}��w�6���c�����_�<f��9Fxx����_~�������h���q��e�14:t�`������m���ys�w���s���z��#G�4:d����Fpp�����;�������j<�����#G�i��6��8x�`��f��e��]����O�S�N��/7BCC����;����Z�z���O+W�4$�V�r�o���Sx��?=�:����KF}�>���Q_F��E7�x�1n�8����\�A�����}X*�III1$[�l1�0�����f,_��y���G
I���;
���O���g$'';�Y�h�fdgg�a�>����M�k�u�]����+K�/^�h�h��HHH0z�������/{�1�G���w8Ftt�������---�4�}�]�0���#�$c��=�c>��S�b����a������j�r�.��-[�t����;�!C��\���n2|������2������m������#
� V�����w��3���7Pg��������G}���<tq��+W�h�������s���������;w��d�s��IRdd�$i������.1h���bcc�1��s���k�z��9�0`����u��a�1��L�9*S����!C��b���>�H]�t�w����(u��Io���s��S�����r�������\b�.]�8���������{�n�1�z�R@@��������������O_����6l�����J����_j���4h�$bU3������*���7Pg��������G}]6f�KE��$�^r��y����|8KR�z������Ry�����I���{w�m�V�������EDD�[0���E�(_I���������&�����������g�G�~��7�h��Ej�����]��c�j���Z�t��_���{HNNVTT��~EFFz$�f����?����[�Z���fS�N�4i�$�9R�*����NY�]��s����KF}��k�Q_����R����G��jk��q:t���o�������3g��C)!!AAAA�.��9u��E�>��$�S�N:t��^{�5�=���3���_�����-[�6m�����4i�4h@��:�x�����v�5J�t/�S���Vk�Y=��;���h��;���O>�D�6mR��
�����u������_0���E�(_I����)88�R�z���JII�
7� ���k��-z�������z������__qqq.�Z�n���DI��kI������999JMM�H<��)S�8���k�N��{�~�ag��*����NY�]��s����KF}�>�k�Q_����R��5	���s����a�s�����
���y�a?~�V�Z��7�I�&.�;w�,�������+11����x<x��?VBB���������.��?&��!�}�����u����.]�h������U����Z�����R�F�$IM�4Qtt��=���k���.�JKK���{��l��Q�C7�t����[��n�;�IHHP��-U�V-�1%���233����1n�Z�p8$���).���U>=�:�=�����v�u��).Z_{t�9�x������@c��%��#G�x����p���2;v�nl���HJJr����t��?�����56n�h|��F||�����I����k��1���[�R$S�L1�=j,\����H*[��
k�*���n����f�2N�8a���;FHH����o;��3g�a����6������
+r��N�:�w�6�o�n�h��e����4�^�z����k:t�x�������BKn���s��5�=j<����Z�e����u�]�\�e���F�:u�G}�yLu�����������7$/�����~��o�5�\qq�,�.�.zuv�Q_���}�����.���,X`�����7�h�����E�IE�Y�x�����/��_�Z�j!!!����n$%%��������A����`�N�:�����v����M�6;v4��M��\#_e���>����l�m��4Z�je���p��p8�'�|��W��h����8~���1?��������

5����1c�/^t9��/�4z��a�]w�1g��Bey��������0��ic��?���
�Qzz���C���FPP���iS��'�pYF���j��ME~>�=�0s���������X^��eG}]<�k�P_���0�a�g��@i1 A�H�0tL� A�H�0tL���n��M�4�������e�X������`:��@�A��B��K(�5�$��	:IRvv�y�]w�u�Q��n��&m������%������k��uk���j���JJJr�����'*""B�k��c�=���G���n�$�w�}��e����/��"�����O;��w�^u��E!!!�����?^Aw@�@}
Tm$�$I������;��{���������
8P'N�p�����s�������u�V%&&��Gq������;�h�����������?�������������'%%%)))I111��O<��^x�}���������
�w*�k�j��u�^bb�/^���D5h�@���#�h��5Z�x��}�YI��n�k���f��I�����3�8��`�M�:U��~�$��W^������������EGG*��Y���woI���?�!C�(++KAAA��q*�k��#A��*77W�_�����l��]��:$$�Y�KR������"I�p����;�o����j��s��r8n��}��.������������b������222d�Z�w�^Y�V�}�����m6��>��"�0<V����X,�����:�k��c:u��I���JIIQ���]����(����W�����������}������\��������hA����^#G���Q���/�S�N����a��o�^C�q�<&L������ys�j�J,�O?��|�.I�7����u��i���*22�[�@�B}
T}���$-^�X�F�������e�������g��R�'{�������N�F�R||�BCC5`��Icy�Y�V����n��JLL���P%Q_U�����(��p�u�����;5s�L_��0����o��V���S�������W^yE�N����{_
���0/���???-Y�D]�vU���u��A�_�^�[��u��������;&@:&@��	��`$��	:&@��	��`$��	:&@��	��`$��	:&@��	�?����M�IEND�B`�
int_sort_bench2.pngimage/png; name=int_sort_bench2.pngDownload
�PNG


IHDR�X��%9tEXtSoftwareMatplotlib version3.9.1, https://matplotlib.org/��!	pHYsaa�?�id�IDATx���{|�����f��,a ��I��J�
�[E���Z}D���So�\j�B+�QK�V)���(D*P�(�?�JB�,!$�����4�,	!��,��_����9;s&9�|��9�f�!`�(�+�X �`t,�����:@@��X �`t,�����:@@��X �`t,�����:@@��X �`t,�����:@@��X �`t,�����:@@��X �`t,�����:@@��X�SO=%�����"��R����e��4o�<���9�����:@@�����t�M7��r�U�V�4i�������������������eK���?����4H��w�������@N�S����y���,++�SO=�/�Pqqqj�������{��(��/�c��������^��[�6�7�&�����t�M*++��9s4r�H��7��=����?k�,�;V�;w����5y�d�[�NW^y��9t����^����_�g�}V]�v���S���o��x�^���?������O=����4i������g�o������~�������O?����k��1�x<��=�\mv@�:t���_]�4a��\.��w���)S����'�|RO?��������1c��w�����~����������o�]�t�]w�]�vZ�d�F�!I����u��i���z����}��GeFP�������_�E���.]�h���Z�f�~��6�7�s=�X��	�^?�������Z�J>�O7�t�������������{����&$$���n������e�]�����m���������9��fz}��7���$
8P�����t,�s��A�;v����(���_QQQ2�F�j�#�uZZZ����E}�����{��U�.]}�?222jK�JBC@ ���}>�l6��~�m���e�^�VFR��������)#�`q_~��:t����W_����}�����2C:t��^� ������l�"��S�4�A��-Z������$i��3f��v��O�^���0}��w!���nPQQ��{�������������O�^{�����7���_�-����/�X����Ok��i������:5o�\�������u�=�h��)!�o��������zH}���c����w��}�����G7�e��������/z��'����*::Z���~��_��?��������_�Z��O�$���+;;[�^{m����������Y��|�r���kj����=z4�u�`6��j��g��:@@��X �`�����|><xP��7��f3�:Lb��=��m�**��[+ �71Tzz���`PZZ�����MN���%U�#t�\�����h���������0�.����6�P�f*�Be�6�v������0�����r�,��N�\.����h3m��� T����m�G_��
�:@@�x@H�^�<����H�G���*++���m�s9���F=@�����9r���D,�0�����er���$���2\� ����p���"��I�;>�O%%%JHHPTT�=ql�JKKUXX(Ij��M��
�����^�?��j����D,�����
���5j@����xIRaa�RRR��$�iU?s�t:M�	BQ��b���@@Pok�,��" �h���_/��V�����-SRRR������ ��^i�z��������G�A�i���fWaB@6�VI��K?��t�-U���������
���a
�W��������
6��4�VI7�(}�M��o����X!���\'NTJJ����4`�m�����,[�Lr:�������w��Y���B�����i���8�k�Ns������������r������o�Y��������W/����W��;��C6l���e��d�������^��Xa�j�4i���7���j�|)-MZ�P3�����C*-�_Y�W�8��=��f��q��>�u;�U���GyD����^z�%�k�N�<���
����J-[��Q~��-����4g�]w�uz��w���O�y����7z��7����U:p��8 �j����G+!!A��������������o������������k�i��U���j�������{���1c�$������E#"�V�wMO��\}�t�JB:@�(-��X�Q����X��%%R�f�/w��1-^�X��-��#$I/���rss�d�=���5��p�B
>\�<��$��/�|�w�y������S���5`��l6�k���o��u��k�������?_n�[��-S�=�u�V]z����z����?����9�N���������w���[uW�TwM%i�d&	@���w�<�������p8t�e���/���=_|��.����mYYYu���;����;��KM�8Qk��
:^zz������233���T�v���C���6��g���Nd��U�`}NgUOv}>rr�w������l�k�%�\�}��i���:~��n��&�x��!�Y}����w�M~~����l��
3�����y������6[�����=�^_;vTLL���}��s����[��r��n��i��-A�>�������r���o��7��o�Q��������[7�3����$i���:r��233�<fLL��1m2��6m�"��^5)��7V��Cz�do4l8��z�����~X-[�TFF��y����������='NT���5o�<�=Zk�����sI�?���i���{+**J����RSS����!C��G����[5�|k���������o�:���}{m��E���WBB�Z�l��(B����"l��+z��6m6)=���=c�TM
��Nd�����,x������t�����K.�W_}�5k��E�������^|�E-\�P_|���]��{��s4o�\�<������K/�T���WNN����d�������E�4h����zu��A��_N[�)S��n�+33S��w������{��`3����\�v�������b�\����zw��������x<���h���r8fW�6�P�f���f����o�>��g����w(?�j����
�snU>�On�[.�+,=�u������!�����U�����U
i"����vi� �kXC�vc�H��K�;�$IO?���}�sM�����Q4��MfH�
��~��Y��� ���� ���� ���� ���� �����3�l�2%%%�]
4�h�+������������������H�c�"���1�
h��hty�y��\�y��)?�<�Ey�y
~�A�i���z��G��eK�������
*s����g?�y��'������Z�|��$���Xv�]��$����e��������/������OY��+W�G�j���.��egg���c����1Ciii���U�^���;�����~�l6��/�UW]���8���+���;U\\,��&��V��y�0
=�MGQi��*��,SVYVg��x�����Y3m��E�<��f�����\����G*,,��o��m����K.����u��a%&&�W�^Z�~�$i��]��l��c�JJJ$I6l�UW]U���������D?��O������7����_/��/\�P�>������O?�T��
���^�/��2�8�>��&M��/��B?���`��\.���+??_S�Li�������"�a:Vq�^�=��u�����:���={���O>���;k���������['I��i�>��#��������:w��y��)))I+W��T�_���_��C��[�n��i�[]���Rc��Q���u�Ei���JHH�$��7OS�N���cu��E���/��W/-X� �8�'O��1c��C�k�N�����lJMMUjj��x�\<���"[��T	s6X:�^�J���YL�z�g��A���i���BI�'�|����j�*������w�^I�UW]�%K����j��
���Vjj���_��={������A�j=��_����G�������u�m��U�Vr��:x�������������W���o�������.�#���f����$����M�6��U��~��W������}�6n����g+55Us����_��m��s�����n�+77W|����Y�^xA�f���-[j��K�f��!��D@�i����p�dZI���,�Y���MwnR��^�:wC���KTPP���h�o���2III�����{�99u��U)))������[o�rx{5������+++K�&M��_���W���R��m���������e�]V�1cbb��zC�^X���d�����l�<y�$����z����K���+##C'NTqqq���gh<�c��&\��#�D6���f1�������1���:�����0d�eee�������k��~}����/~���]�z��W^���-[�[�n���Oe��-�={�>��c�����7����u��M�������������h��=z��G�s�NM�4��z�o�^%%%Z�n����TZZ��
��tl��U�?�|�s0���5o�<eff�����������'���t�R
>���z�M�!��
l6�rrr��_�Bw�y�����(55UW^y�Z�n�/w�UWi��A��4H�|��)�?�$����7j��r��JOO��y�4b�I�w����?Waa�233��o�r�|�+��B��{�n��f}��wz��'Yj-������D��z�^|�E=�������w�k�����cG��5K��v�*++�Q%%%)555��nt��#������:�Z���S�3���]�������^7o�\���o�����������j��`������[�n�u�}>��n�\.�TT��|�I=������}�����~���Z�xq��G� ����	4j�(
2$(�����X.�+(�W�g?��.���{�����;O9����\�����n�[���x��x��j��aT=aQY���CJ��U�Y��."m��� TM��x<�!����\-i����}_���y�3Yi�������:hW/����a�x<���A��B��4�0Z�b��o���[���lQQ�f���{��'h��3t��W��tj�������TRR��'�z�9s�h���5��]�VNg�M�q&����]�>SN�7���%77��* ��f*�B��Ltt�RSSURR����3:F�-II���,S��t�;z�hX�SQQ����k��������3��c3N5V
�������rss���4H�z��1$��vk���j����x��KB���'����Ku���Z������������a5f92J��k�/���;�����x�����C�����F�A�h3USj3eee:p����o���8�������G��y����T�����~��������n%''�G��|�����m�TXX�K.������j���z���T^^.����G�j���j���V�^}�_��_~�f�����r�����[�v��a�"v{����(��"��
���6�P�f���f�^�l6����E�����^��llQQQ��l���s��F"RQ�<X�v�
�v��w�k���:u��v��n��
���X�����3�s�N�h���nua�a��&��7W�����5k�L�Z�R����v�������R����r����o�;�<��v����:t��������8���j����2e��t��� ���ld��Y��}�vm��E���S��}���S����p8�h�"=���2C�:u����u��w�Qe4!���KKKormP_��1�=2�Mt�z��
:������k����\��� r��v%%%���P��t:�2���������Beee���a*--Uaa����j,�k"��tt�����*I���������+>>>,78����?7X�� ��l6�i�F)))�x<fW'"y<m��QW^ye�;w8��G:LC@�Lv���w��v�*++�s���a:�a�@@gr ��4��@�!�@�!�@�!�@�!�@�!�@�!�@�!�@���:,�t ��Dq�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:Lc�U%s:�a��t@:�a"�A�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@��I���+�����'�����i��	j���t�
7���CA������Q��t:�����~X���a�}� �@�[�n���?��={m�����o��W_��
t��A�3�����j��Q����|��^zI��-�O<�Kh�@@����z��z����E����b-Y�D�����W_�>}�h���������J���]���w���_V�^�4b���9S�-REE�Y�t��A)��
45&L��Q�4d�=�������m�����!C���v����m��Y���������G�n��_f��a?~�>��s���������U^^��v�%I�G��1.��|>I��������ZD��6kv�E��� T���6�PY��X�F@�+Vh�����uk�}���QRRR����[����_��p^��z_m����������v�Z9��3�������:k��������uAd���5�
�0���6�P�f*+����R����������4i�rss��N�6M=������Vzz�����r��V��l�X�9#��F�lgj]<�rss5t�P9���@�A�h3m��R��]� ����m�TXX�K.������j���z����f�UTT���#A���Rjj�$)55U}�Q�q�gy�.s���X������p8L��`�{%IQQQr8�����
���6�P�f*�Be�6c��Q��������k�.�������o_�z�������[���={������,IRVV�v�����B���\�\.eff������@=�a��ysu��=h[�f���U+�����K=��Z�l)���x@YYY����$);;[���������3����@�=��&L�Pk/��� ��n!�����n�A���6l�~�������v���[?~������Y3�7N3f�0��g��t�_�>�u\\�-Z�E���=���SNNN#�,<��3�0
�0
�0
�0
�0
�0
�0M ����X ��4q�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:LC@�:@@�i�A�:LC@�:LC@�:LC@@S��J6��q�����&����j�0
M��UR�������?����V��U��jt�����`�*���o�	����U�	��F@�i�8�y���I���[�m�d1���h�+���f���D@�Y|>��L*-����|��3��vW�TC:p@��?�A��v��(:LC:�RY)?�x������V��ov
`t����C�x& ����"��'9��4kv�}u���S���N�6m��`}t�����#0d����Lt8������g��?��%������)=�h��p���k�Iii���
nD:LS��
�70d�1{���n?�p|��qq�����.-\X5[�����i����9��C@�i�AVr������G���=����k�Z������^SL�����h�#r�s��#�\Y5���K���U��1cL�,����@}FU�m���N�_Yy&��K����+>�az�O�?>^���}�3F=Zz��J���N��K?�A4=��?Y���@���j�������<��s�^}��W�����7��;@��U�M��.]u��c���UW]L8G
t�sTee�Ov�G���r8������������SN������� m8;t��!�����%�km��R���j�g����:���0
`E�Q�+�v7nx��U
����m8[�0Y�x�/^����K�.��"=��1b�����:����������~$I��2�����~��7Z�����j�����a��S-Qu����2����
���&iii�;w�:w�,�0��K/i�����c��v�������/���~��_i��A��.]�����_'%%�����������j���N.��KT������Y����T#���5�\�z��YZ�x�>��C]t�EJMM
��z�j�t�MJHH����T�l�"�@x�j����}>�%��\COV�=��y���v�\.3,���n���W_}U��SVVV����m���;�h���&L�����g���t�����;��u�{$ �@�KT5d�������[�������?�G���r%$�p;z���KYYY*++SBB�V�^������,Y�n����+��>c�]}��r:�Z�v����>���h����<gyy��O�v�%I�G�������|>I��z
����P�Nh/���m3�-Qu���F���x-;fz]�����U����{���0jL��t'|]�
���G����5�s(KT�������BE�A���f�P����RQQ���<k�������
6������M�6z����������=��Z�t�8p�2O=���O�^c�����t:��b��Mm5o�����?z��L���bRee����u|D������sYYt�m'���2�SR��x�Ull�?W}�W}��U��=�QY�������oP���T��r�����r���D@7��!C��cG=����m���t�]w��o��y��W��������?�������z����UTTd�?�+|;6Vz�n���� 2x<���j���r0��eU/Qur�ru�qu����N�e�l;��^o�z�m6#h�����'�<;��	��{���n���f�*k��BE�A���f�n����	��w�|���,U
o���kO�%i���j���)��$�������p��!:�z|���� �X��F��%�sm����^��KT���r|�OG���c�6JH�:�g�ccm�����?P?��A�h3�����GM�0�6m�F����=zT��/�����f�����J7nTNNN������:t��������8���j����2eJ8/�A1I�z��3Yz�����`'/Q��������UN��9r����������Pc��U~~���gO�Y�FC���������4egg�x�����E�����0u��I�����w���hPtD����j��g���:��|r��xf�=L�,Yr�2�g�����k�7|�p
>���e*z��^�������6m��	9u�m����VX��!{����m�����kZ�J�4I������4i�Bi���u�j[��l���c�*,���h�����^SLL���|b������X�J����7,���j���
�
C��h���N���Kk�$%�ro�5���9>^���&	j�4��x�U=��}/���_5,��g��&@������Vw�ox����'�l�����r9���D"�!�Kn���U��'k�Ma�4jT��!:�qf�>�#6�a�l{<�*+��K����Dt��)�#G����?>�����d��I�������X�9�w����wK��=m�T}l�~��/[&
�P�`%t�&���S�~r?t���IK�23��.����)u�&�hQ�z��U��������?p`�]��a�t��
����O�EE�~_�v�q��������R������������������0�U�aT
A�-��}������:�w�*%�z�:�S��Zm��/X����?:L���{{��?�)��W=�=p`���t�@�A����=QQR��� ^��t��`���#��8����0Mm=��V����pa�{�}>����gK���j9�����c�K�;��^X��x8��L4Et�����jU�3�'y�����+W�t�W�����e_|!?^�9���}r��Y��i�k��"��4't��������
���=�T���������RYY�����z�� ��#�~�&:Lsb@��?�����0������qqU3���;t��i�"�91�����=J�F�x��L���@@�iN�m���=3f0��sS��������@h?��&��W��s�9��n�ZJ��r0�����������1�+��,��Vs�58��:Lsr@��V��>''W���m[5���s�::LS[@_������J���:�Y�������_0�99������U��1�^`:Lsr@_�������E��������9��4et���t�W���z=l�yu��a�������R������[/0�91�Wo<Xr8���������'�cx;�����Tt�[����k&��TE�]4]�����/:u�.���
���A�e0�@SF@�i|�����S�:L�j�4f�=h���Wm������[�J��F��0x{~~�vB:�������z�I���V�lA���I�'W���������?�o�9�~���*M	a������\A@GX�i����\A@GX
(��I6[��m6)=��4%t���.-\X���f���T�����&�/V��=�r��r�������~����A��lA��{o�1���4j�(9�N������Veee�/���#�\)�m�=-�j��1���mv����4��;W�;w�az���4z�h���C]t�$������3��q:����^�F����T}��������c�p84{���_��3F9�R��mQ�v������9�t����k�	z=k�,-^�X~��?�;�N�������k�j���z��w��uk���K3g����S��SO)&&�������R��i�HC���s1��^�W+V���c��������+�(99Y��w��i�TZZ���y�f���C�[��o6l��n�>��������A�]�v)++KeeeJHH�������)I���[��];�m�V�~���N��={�h��U������p.��������,//Wyy������$y<y<���PU���z r�f*�BE�A�h3������`6�0��CC���P^^�����r�J������
�!�D���?4x�`}��W�������}���Z�f��Lii��5k����1��s>��S�>}z����/z�@�RZZ�[n�E���r�\fW"��j��!���������;������;6l��x�	�����s����}�t�h������w����===]EEE��#�x<������C��!t�m��� T���6�PY����n%''�-�!�&��|A��D�A�M�6����,��5K���JII�$�����r��_-66V���5�;�!T�R]h3m��� T���
m����&z�L�6M#F�PFF��=����k���Z�f��������k���j���>��S=������+��gOIRvv�233u�����g�QAA�{�1M�0���,�0),,���c������D���Sk�����Cu������Z�`��;���t�p�
z��������z���4~�xeee�Y�f7n\�����E@�%K��r_zz�6l�p�c�k�N999
Y-�E�:@@��X �`t,������h�+��%�8OE�E����J�-��;]�����H�0��`
z=���WK�.���{�p�B�������VFF�.��"��1������.*�,�����q�q�s�B:�&�!���a����C[�l��U�TRR"I���O���O�\��RTZT3��������M	�4}�Q=������ULL���W_�?�����%����k������SRRTTDO/�a�O#))I���5����C���	5�������XS�NUAA�l6�|>���}M�2Ec��5�z�s�4f����]�*==]%%%�����W^�+��B�=�����#Xf�4bbb���/����g�}������[�;w6�j�s��222�����g#������:�Z���S�39��k ���aZ�r��{�=����_�j�I5�<��s��:���v��|�G��k]��JIU!>#�!��iL�<Y�?��~���u����lfW)�e$f�x3G3I�-.�%m.1�Z`:�i��O��U�4r�H��r����a�0�&`>fq?���D]p�fWp�#���SO=��������fW��c�{�
z��!��q�M7����RRR��}{9�����o7�f��!�@@?�q��i��m�����$��U��������]k�������9�!��3�����.��ev5�Iq��i<���z��G��~��r�a�8`��i�v�m*--U���t:kLw��a�j�x����`���p�c�;�Ok��qfW���w ����v�'�s��u�e�3�����6-Z�P~~�RRR���T����a�f�����P�s��@����?��eKI���K���.��T���)//����s��Z]u�U������{�O��w�i��!<�~���~�C�OVRR���8jt����������C=$�*D>���r:��}^�W[�lQ�^�L�����@@?�;vH�����k�bbb��bbbt��k��)fU���w ���{��'I���;�p�B�Skq��i,]���*���$�$�at ��t��=l/^��={��r��r)++Ko���$����z����K���+##C'NTqqq�1l6[��+V�q9
�?��g��g��%--Ms��U���e�^z�%�=Z;v��a:x��������L}������{u��A�\�2�8K�.����������|%
�?I=�@@�k��&���Y��x�b}���������k���u��Q�f��m�����JEG~LIIIJMM
[��A@7��������c��)++��2���r�\A�\�&L�����g���t�����;��s����r����_��nI������i��9s�C�+++M�"Cu;����h3m��� TVj3V����h��]���RYY��z�jeff�(WTT��3g��{�	�>c�]}��r:�Z�v����>���h����<��9s4}�����]+��y�uwX���g�)�`��uAd���5�
�0���6�P�f*+����R�����f�
���
������X+W����{m��!(���n
:T-[��o�!��q��=��Z�t�8p�2���������H.��a.����(�����#�����4�.��G���:th��6�j���6�P�f*+������d��]���0���Q�N�$I}�����[�p�B=������G�j���j���V�^}���_~�f�����r����Z&66��}���_Q��E�v��uAd�B�Ed�� T���6�PY���}~��2k&��|��m�����l�����7�P\\�i��s�N�h�������zv�z��d��i1b�222t��Q-_�\�����5k�����T/����n�2���;Ov�]o���:�~��)..N����={��L�b���9�A�z�j������Wbb�z���5k�h���Z�~��l�"I�!�����������ph��Ez��e�:u���������6�r��@=L�,Yr�}�
:m/����5|�����"x�c�;�a"��@���$@@�0��0C� ����:�a�A�:L�w ��D�g��t���:=�@@�y�{�tXC���1��0
��@ ��4q�:L�w ��4��@ ��4�!�����q�t ��<��@ ��4q�:L�w ��4��@ ��4q�:L�w ��4��@ ��4q�:L�w ��4��@ ��4�t����?��w ��<�=�:,�!�@@���t��I� ��&�/V��=�r��r�������~�����L&LP�V�����n�A�
:F^^�F�%�����=��������=L���4w�\m��M�����j�=Z���$����o��W_}U6l���5f����^�F����
}��z����l�2=��f]�Yc�;D�]����k�	z=k�,-^�X~������d�-_�\W_}�$i�������>��C���Ok�����������u������f����S�����RLL��uV�tx�^����:v������m�6y<
2�_�k�����������_?m��Y=z�P����e�
�������?W���k=Wyy�������n�$������4��Ou����5�.��������� T���6�PY��X�F@�]�v)++KeeeJHH���������;w*&&FIIIA�[�n���IRAAAP8��_��T����������v�Z9��������M�$i��}���1�.�,���fW�6�P�f*�Be�6SZZjvpzu��E;w�Tqq�V�\�q��i��
�z�i�������v��JOOWvv�\.W���t���F*��wh��W�4�.��G���:t����A�� T���6�PY��T���u��(&&F�:u�$���G[�n���u��7���BG�	�E?t��RSS%I���������W=�{u�����*66��v��a�/�h{U��E�L�"��/"m��� T���
m����&fq7���Syy����#���u�������Gyyy����$eeei��]*,,��������Rfff������1�;��.��M��#�����G�j���Z�~���Y���D�u�]z�����eK�\.=�����R�~�$I�����������g�yFz���4a��Z{����&���;v���������={j��5:t�$��������t�
7���\��
��~�;���v��z�-�?^YYYj������3f�uIg��:��=\�,YR����8-Z�H�-:e�v���S��3�x���X���N:�a&>'��a�8 �`t��Y� ���0��0
=�@@��0
C� ����:C������t��z�8&b�;�a�2kq:V@@�i�t��!�@@�i�A�:@@�i�t��!�@@�i�=�t �`t��?��g����0��0
=�@@��0
C� ���0��0
=�@@��0
C� ���0��0=�@@������0C� ���0I�a:z����:��d��9���K��ys���������={�����/��V������/W��+V�qIg��:C���.6l��	���*77W�G���:v��$)==]���A��OWBB�F�t��K������L�����!��h�+�T���;A��-[���m��MW^y��v�RSS���^�Z7�t���'%%�(��Az�MS\\,Ij��e���m���;w������o��	JNN�e�]�?��p����A7���������u����2K�,Q�n�t�Wm�1c����j9�N�]�V��w�JJJ4q��Z�S^^���r�k��-I�x<�x<
tEg���U}��L�"Cu;����h3m��� TVj3V����H�~�`�����o��M�6)--�������M�6z���������c=��Z�t�8P����zJ��O��}���r:�gv
$�(G/|����4��TS�45������[T\\,��evu z�������[o���7��%i���*--���cO{��/�\3g�Tyy�bcck��6m�z�!�k�����tegg���p�G��o���[k���������(77WC����0�:����6�P�f*+������z���x@�W�������C�S�]�d����Z�w�y�=���;��E�Z��$�������p��!����g��uAd�B�Ed�� T���6�PY���}~�D@�	&h���z�����ysH��/��W_i�������q�7�|S�R�~����\��=[S�L	�u4$�A�z�,^�X�4h����K�.�w����?�Aiii����q���E����a������������1��hX�aR�^���gk�����>|�����X���4�!�����q�t ��<��@ ��4q�:LS=�@@���t��z�;��1�;�a:��@:�a"fq�:L�:�@@�i�$�0=�@@��X�0
C� ���0I�a:z����:�a�3�q:���!�@@���A:LT=�@@���t��I� �����t��u� ��������t��g���1��0
C� �����Ag�;�a>z���Uq�a"��@�a�8 ��t��&bt ��4q�:L�$q@@�9s���K/U����������N{��	*3h� �l���{��7�L^^�F�%�����=��������48z�@�6�M��
4a�]z����������*;;[�w�V�f������n��1����t���z�5j�RSS��(??_c����������z=
�u� ��&���N��e��)%%E��m��W^���t:���Z�1��]���w��w�U�����W/��9SS�N�SO=����F���v����<���|�3Y�a���$������-[m��W���/+55U�\s��q/������G�n��_~��a?~�>��s���;|�N�$.�8O]������S������������&��|�<y�����������r�-j�����m�O?�TS�N��={�j�*IRAAAP8��]PPP�����U^^��v�%I�G��A�+T����|>����TVY�w��8���z���6kv�E��� T���6�PY��X�F@7��	��g�i��MA������=z�P�6m4x�`���W;v<�s��3G��O��}���A�������H�JJJj|/Ne��M�w�7f�rss��"m��� T���
m�����*�$�0������[oi���JKK�����_.I������cG������>
*s��!I:�s���M�C=��v�������l�\������U��Wj��L��u���?8_�Z�E\%�%U}�ORRlR`[|�������G����R^q��;��)���o����{<���j���r8fW�6�P�f*�Be�6S=��A@�0��h���Z�~�:t�p�����S���M�����,��5K���JII�Tu���r)33��c���*66��v��a�/Gt�����k���|�oK�
�\�X�Z����w���B��������7��)�8O����3��d����B�A�h3m��B�1�����&&L���������y���g���{�j���9r�Z�j�O?�T>�����J���S�������L�~��z��gTPP��{L&L�5�G�P�A_r��Nh���������\����	�~�sL��.w�]����_�\�f�f����S��U�>.:�^�(*-��3�E�Et�E@���K�
�}������;�w�}W,��c�����n�A�=�����n�[o�����+++K��5��q���M�$�����W�^���%�._��
�'�?);��]^5������y���7!�7.:��=�'�>|�p��pn!��I�Z������
6��8���SNNNCU�T�e�N��91��4KQJ����[��TqY�?������?RvD��U�)�$_�%
3���?����#�!����{�����gs�([T�|�s�3d(�������>���L[�������J���B~����]��;���s���}���O{�#eGt���\���lQg}� ����B����{f7)���#����h��%�!�a��!��a(#1C{���My�t��[���L��{/h��dgr�<e�RR\�����U~{�v�y��i������s����<�x=�~��V�r�i?���
o������+}�5��3|*�,S��~��^�?�C���	���$�M�o�0��Z�0��������j�������/5�Z���e'�n���j��ag�Ou���S�]�w�c����w�c4@j����fF���E5�c&�d`T8��a�	h��M���L���fS�=F1��=���QNQ�F��eA�>o��d��g�
���q�Q
��r�{����lL�Q���qP~�\I�&�o��Q�F���t�8q�hz�0]u@7C����3�V@��3��b���e���yVe�*}���d8����������J_�*}�:^y�����?�6���A�dk�	�&C}FA0����a�o��@���k<zP�Q�Mnx{�3�E�E�,I���zl��?�C
���F�3|��������T�K������Ec�d��|2C�*���VH���fo���t�A^��[������m����l�
�9y����>m���p�Q%Se$f��z��E)6:V��=mY����������XD}�!��
�H�3��p�:x
o���^y+�
31�v�l���8����L	�G@�i���������%5����&���h[�����x��sV|����p�d�fC}���&�<��
=�c}?31$�s�3��?���� �D��ebH3������75x�`����d����P��T&�l��g�~�a�'G?Q��	����L�t���e��������Q�C��Uq�GQ����8���������$IW�_aV�M�=����5�mn��"BQ���a��P���3���Li�����L��<�(�0�d8��j�[^q��<���d��
|�=��!�����8���H�.lu�R���Y-"��:1�o2Tx+t��a�5�;�j�]��6��A�Rh��Nw���PTZT�J=�TVY���":�0�t������9����21�<�rrr4r��:G]TO����l8�:�61�%G5���.wY��Q);b�e!��a:CMw�s�4�81��4�!�7N���P7:Nv.-w��D@�i����W���o?�$���of�pV�P_�]��lo2����;~o��"B�a��!��
$I�9�S�������p-w�=;�ev�t�<�����
��"��2x�@SF@�iN\]�����9�-��d�E��Y&.:N���0�V�3����<�i�w{��b�U��l���dg2k@������=��QQi�$���R�6m��]��������<uy���*����{��oI?IUw����_R8'd$f����x<�w��wjo9��C�VE�E5����*��w�� �`t,�����:@@���dg�����,�dgr�j�mv��d$fh��{TTZ���Jm��IPtt�)&;����ab- �����e$f���(�������p8����!�X �`t,�����:@@��X �`t,���D�]��a�$��mrM$�����R��n9���@�A�h3m��� TVj3���:#�|�&��������t�k�
�=���D��I6��%M��������ys�l6S��v�������r�ZD�BE�A�h3m��R�1CG�U��m���V@z���4����r���	��6�P�f*�BE�A���f�9�n�`t,��������'�Tll��UA��� T���6�P�f*���$qX=�X �`t,���,Z�H���W\\�.��r}��GfW	
l��9���K��ys���������={�����i��	j���t�
7���CAe���4j�(9�N������VeeeP������K.Qll�:u��e����m.���;W6�M�'O�o���d�~��n��6�j�J�������>��c�~�0��O�M�6�����!C���_�������[�r��������K%%%Ae>��S
8PqqqJOO�3�<S�.�����v����8���C999�s�8c^�W�?��:t����xu��Q3g����&�f���7��k�Q��me������-h���G}��c&X�b�c��0>��s����6����C��]54�a��K�.5>��3c������#�������_��{�5����u������_?��+������4�w�n2���c����c$''��M������m8�N���2v��m����5�v���;�����"�G}d�o������1i�$�v�Nt��a�]�v�w�al�������m�Y�������e���k$&&����O>�����k�:����>|�q��~��������������������h���q����}����?�����7��y��������3�<c����x�����a���+<����Y��V�Zo����o�>��W_5�����f���������j�*C��z����Vj��"������	&�_{�^�m����9sL�[aa�!���a�a�q����p������_|aH26o�lF��QQQFAA������
��e����a�<��q�E����o6�
�M��,G�5:w�l���W]u�?��fp��S�8�~��g����������9b������
�0���w���[���������f3���[�0�w�����E�>w�.]��o��&c��QA����������9��D�5j�����4h��1c�[o��0������>�SD��#�***�m�6
2��-**JC�����M�[qq�$�e����m��������]�*##��6o��=z�u���2��
�������/s�1��T�6y&L��Q�F����fp�7�xC}����~�#����w��z�������������ebb�.����6�����}���2DQQQ��e����W^����a��i��=�����e�jW��+��B�������/I�'�|�M�6i���h3����G}���C@G������,I�[�VAA�I�Bc��|�<y��������K�


������'�����Z�J������n?~�6aV�X����k��95��fp����Z�x�:w��5k�h����8q�^z�%I��y]?����������V��-�]�f���G���cu��U�C�{�����u���J���nVj��"O���4L�0A�}��6m�dvU`a��I�������8��������o_��=[���wo}��g����?�7�����������W^����u�Ei����<y���mK�`:z�v������5f]>t��RSSM��������zK���������SSSUQQ�#G��?�-�����V���U��r)>>�6A�m����B]r�%���Vtt�6l�����7���V���i3��Meffm�������$~�u�,SSSUXX����R�n�vE����~�����G�~��z����vh3����G}���C@G�����O�>Z�n������u����eb�����������W����:t���O�>r8Ama��=��������,���+�?���\�\.��YYYA��.S}�\�<x�v����;w�?����[o���5m'���������]�v��:(555�g�v��e���6s��m���_����|>�.��r��7���������K�.j����L]�
�PZZ����?��v�|>�$��f��Q�� �=K��+V�����e����w��s����4�2"�������Dc���F~~������_��{�5222�����ldeeYYY���Kfegg;w�4�y������u���~����/�E���dm.2�8��a�f���>2����Y�f_~����+�N��x����e���k$%%���������G��uI���{[�l16m�dt��9hI�#G��[�6n��v���>3V�Xa8��K"EGG���3�����'�d�,7n�q�����Y[�j����l<��#�2���������;�;v�����;v�0���k�0��>�SD:L��������0bbb��.���������Z?�.]�/s��q����3Z�ha8�N����7�������~c��F||����l���?7<OP���{�����c\p�A��F��L't�N���o��w7bcc��]�/��B�~��g<���F������Xc�����={��|��w�O~�#!!�p�\��w�i=z4��'�|b0����5�?�|c���5���������������.2����7�����nc��IFFF�g\p��/~�����h3M�{��W��/���3�Z��>uAd��a��w��:@@��X �`t,������
���������_/���#G��]�9tP+���� �`t"Lyy��L����?_��5���_������/[�LIIIZ�f��u����
>\����2����8q������U+M�:U����u�]'I���;�a�-\�P6�M6�M�������m�����N����
���'LW����@������y�f�X�B�~��~��i������/�eJKK5o�<��O�������)S��������+����K������������7���*++Kw�}����������t��_��z��g���+::Z?��O�r����������<-]�Tyyyj���$i��)z��w�t�R��=[���x���������P?c��q~���j��i����%I�=��rrr��#��������5k����*I���>�Q�F���Lqqq�s�4t"��]���zu��m///W�V����N�?�KR�6mTXX(I*..��C�t�e�����v���G>��^����g��%���P�_�D@ �����n�k��m���A��_;��}6�M�a4X=N<��f��z�{P;�A ����[^�W������S�GmC�k������[k����m^�W��o*#������F:��/������c���g�U�������G���S��=5j��z����9s��S'u��U���o�����{�%�}����e��������l���.�t"���K5v�X���?W�.]t�u�i���!=�=u�T��'?���c������
6,h��)S��n�+33S��w�����r����| 
D$���n�����n���3��MC�h����k�]�VW]u������s�i��}���[��MC�h�����l�2]z������v���w�}W��u3�j4Yq��A��X �`t,�����:@@��X �`t,�����:@@��X �`t,��������d�IEND�B`�
#8Stepan Neretin
sncfmgg@gmail.com
In reply to: Stepan Neretin (#7)
7 attachment(s)
Re: Sort functions with specialized comparators

On Mon, Jul 15, 2024 at 5:47 PM Stepan Neretin <sncfmgg@gmail.com> wrote:

Show quoted text

On Mon, Jul 15, 2024 at 4:52 PM Stepan Neretin <sncfmgg@gmail.com> wrote:

On Mon, Jul 15, 2024 at 12:23 PM Антуан Виолин <violin.antuan@gmail.com>
wrote:

Hello all.

I have decided to explore more areas in which I can optimize and have
added
two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

Hello all
We would like to see the relationship between the length of the sorted
array and the performance gain, perhaps some graphs. We also want to see
to set a "worst case" test, sorting the array in ascending order when it
is initially descending

Best, regards, Antoine Violin

postgres=#

On Mon, Jul 15, 2024 at 10:32 AM Stepan Neretin <sncfmgg@gmail.com>
wrote:

On Sat, Jun 8, 2024 at 1:50 AM Stepan Neretin <sncfmgg@gmail.com>
wrote:

Hello all.

I am interested in the proposed patch and would like to propose some
additional changes that would complement it. My changes would
introduce similar optimizations when working with a list of integers
or object identifiers. Additionally, my patch includes an extension
for benchmarking, which shows an average speedup of 30-40%.

postgres=# SELECT bench_oid_sort(1000000);
bench_oid_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 116990848 ns, Time taken by list_oid_sort:
80446640 ns, Percentage difference: 31.24%
(1 row)

postgres=# SELECT bench_int_sort(1000000);
bench_int_sort

----------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 118168506 ns, Time taken by list_int_sort:
80523373 ns, Percentage difference: 31.86%
(1 row)

What do you think about these changes?

Best regards, Stepan Neretin.

On Fri, Jun 7, 2024 at 11:08 PM Andrey M. Borodin <
x4mmm@yandex-team.ru> wrote:

Hi!

In a thread about sorting comparators[0] Andres noted that we have
infrastructure to help compiler optimize sorting. PFA attached PoC
implementation. I've checked that it indeed works on the benchmark from
that thread.

postgres=# CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- original
Time: 990.199 ms
postgres=# SELECT (sort(arr))[1] FROM arrays_to_sort; -- patched
Time: 696.156 ms

The benefit seems to be on the order of magnitude with 30% speedup.

There's plenty of sorting by TransactionId, BlockNumber,
OffsetNumber, Oid etc. But this sorting routines never show up in perf top
or something like that.

Seems like in most cases we do not spend much time in sorting. But
specialization does not cost us much too, only some CPU cycles of a
compiler. I think we can further improve speedup by converting inline
comparator to value extractor: more compilers will see what is actually
going on. But I have no proofs for this reasoning.

What do you think?

Best regards, Andrey Borodin.

[0]
/messages/by-id/20240209184014.sobshkcsfjix6u4r@awork3.anarazel.de

Hello all.

I have decided to explore more areas in which I can optimize and have
added two new benchmarks. Do you have any thoughts on this?

postgres=# select bench_int16_sort(1000000);
bench_int16_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 66354981 ns, Time taken by optimized sort:
52151523 ns, Percentage difference: 21.41%
(1 row)

postgres=# select bench_float8_sort(1000000);
bench_float8_sort

------------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 121475231 ns, Time taken by optimized sort:
74458545 ns, Percentage difference: 38.70%
(1 row)

postgres=#

Best regards, Stepan Neretin.

I run benchmark with my patches:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.609 ms
initial connection time = 24.080 ms
tps = 6214.244789 (without initial connection time)

and without:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.731 ms
initial connection time = 15.177 ms
tps = 5776.173285 (without initial connection time)

tps with my patches increase. What do you think?

Best regards, Stepan Neretin.

I implement reverse benchmarks:

postgres=# SELECT bench_oid_reverse_sort(1000);
bench_oid_reverse_sort

----------------------------------------------------------------------------------------------------------
Time taken by list_sort: 182557 ns, Time taken by list_oid_sort: 85864
ns, Percentage difference: 52.97%
(1 row)

Time: 2,291 ms
postgres=# SELECT bench_oid_reverse_sort(100000);
bench_oid_reverse_sort

-------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 9064163 ns, Time taken by list_oid_sort: 4313448
ns, Percentage difference: 52.41%
(1 row)

Time: 17,146 ms
postgres=# SELECT bench_oid_reverse_sort(1000000);
bench_oid_reverse_sort

---------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 61990395 ns, Time taken by list_oid_sort:
23703380 ns, Percentage difference: 61.76%
(1 row)

postgres=# SELECT bench_int_reverse_sort(1000000);
bench_int_reverse_sort

---------------------------------------------------------------------------------------------------------------
Time taken by list_sort: 50712416 ns, Time taken by list_int_sort:
24120417 ns, Percentage difference: 52.44%
(1 row)

Time: 89,359 ms

postgres=# SELECT bench_float8_reverse_sort(1000000);
bench_float8_reverse_sort

-----------------------------------------------------------------------------------------------------------------
Time taken by usual sort: 57447775 ns, Time taken by optimized sort:
25214023 ns, Percentage difference: 56.11%
(1 row)

Time: 92,308 ms

Hello again. I want to show you the graphs of when we increase the length
vector/array sorting time (ns). What do you think about graphs?

Best regards, Stepan Neretin.

Hello again :) I made a mistake in the benchmarks code. I am attaching new
corrected benchmarks for int sorting as example. And my stupid, simple
python script for making benchs and draw graphs. What do you think about
this graphs?

Best regards, Stepan Neretin.

Attachments:

int_sort_bench3.pngimage/png; name=int_sort_bench3.pngDownload
�PNG


IHDR X�v�p9tEXtSoftwareMatplotlib version3.9.1, https://matplotlib.org/��!	pHYsaa�?�i^qIDATx���y��u�������1��5K��&�������J;�$R�%���M��}�Q��1v
!��Pb�b0c������Lj0s]gy=���9�u9�3}�1��n.��,�
�.�� ��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�9]rdeei��}*R��\.����/,���'��~�) d��}���s���_~�Ee��u��D� E��d>�����~Z�`���k����?�?���������~�����
SSS���
��A��]EFF�@����7]/Ez?����~��6�~N�!��/����6�!��
�m lC�������R�~���N�>�>}�(::Z����<�\��"�\��k�����R���s���������Y�fi��e��o��v��P��g"�\��'O�{��z��wT�xq�����k����0a����z��__��M�7�|���W;X1�Y�	�"���G�:uR�6m�������_�^j����\�j�T�\9�Z�JM�49�����+==��<55U���3##������a�{�`����6�n����
���m�g��@�h��������v��s�8p@!!!*V�X���J���.��c�����#�9�`����_v�y���d�{�`����6�n����
��]m���f���2H���/z����������|{��C�j����������S�v��o�s!JJJR��m\����Gz?����~��6�~v�a��\:H�_�^�R�z���233�|�r����JLL��3gt���\� TLL�_744T�������������Gz?����~��6�~v�!���G����[+999��{��G��U��!C���`-Z�H��u�$m��])))���w�ddfJ+VH��K�KK��I��NW�� yP�H��Y3����+::�}�w��0`�����G}T�������� =�����9����&M�X�Q�|��+�(  @��uSzz���o�7�x����OB�t�-�e�>�w�9����@.���Ks=����5y�dg
�v�������s��~������6"��X�"�����,��_�}p�c����7h������=[��,�����v��u���j��n0G��R�BW��i���v�r����%�*%�)5m*H?� M�hHT����Y-���?����h����Cv(y�
i�i�J��ai�,���Lp9}Z�?�LT�^]�PAz�!���%v=���.]���s��-{������y�yGJI��l�^zIj�F
	1sD�zK��f):Zj�R;V��A����+�)��o��K��U�"����y�	������O<!�:%-]*%&�^��;�e����Sf(W��f�V��R��}����aY����q�����u��U�����9$���r������������4�������_-��a|GR�Y��P!���}�J��G1=,G��2x�T��	>k�J�F���W\!�v���{fv�@�;�{?x����*�j%����i�	��gBG����cfr{��f�I�Zf��E�������0N�a�Ji�r)8X8������������7d�|s|�����=�=<\�����#�+�[+�� �
���z�^�JM����g�p��s���W_�C2C�����l)ED8W;�
���~���> @2��jr�����oi�4i�>i�Fi�86��������o4��iczJ����$���1��w�az<��%��cB��%����_H?,�//�9c��d&��-k����%�������`��m�&%$��C�:[��*RD��&sX��k${���������� 5n�3\�~�����C@����5���f����r����2�c�I�OK+V��m��U����3f�V�vr�i��:2���'x�]��O>1��
s���fvX�ei�Vi�����n���H3���O���n��5lhz��-�22���� �^/�`��m��I�e��I��/}��t���6LY��I�\�6�Ln��6=Bo�)���h��,x�_5+KI����O��������N��<��3����h���(����������Us���hav�p�����P����f����Qg������%+K��!g���U�����4��j�"g#�j����0���!3B����`��
f�i>,}���g&�'&JH5j��|P�=[JMu�z�����8Q���A3QV�����9,K������������~�l��3\��kL��G�t�r���9\2��g�P��\��c�i�����f�������uOnW��R��R���f^	@>������P���6�����K:�C2�'&�c�"3�����r�P��;����1�H���{�<i�_I�SO1<(�U�(=��Y=��i�i��N3|k�:����
\%JH��*M�jV$�#~���x�m��q�J�m�9]�o	1{��'m�(��k�=��v)*J:~��Ir�}fr{��������f�;�0����i���$=�$��k�����l��n]�d�o�5��o�jvn�Z��Y��re��7~��w�>]��_*[�L��s�����g�1��.�	$��K���������#�ZI���,x������A��� x��(3$����P�M�L{�jevm��Kz�
�h@T����4~��y��[�
���O����+�0s��\.�vmi�`i�b�;����#�H*�0�xq����e�{����_s/�y��g�����5�0��=""�o4�eI?�h���?��}����i���f���j���>���� �������G�������T1G��fa��+s��l�*�^m�g�5����3a�];�1"�z����,���dv���t����0�M����-RJ���;�-�HE��!Y3g���bc�k�1+�-]*�9�p��R@����5�6�-.����5K:|���n�b�\�'�GGK]�HS�H?��t��"0���������6?p�IM��c�(�����$3T+1Q:tH��sH�UW��i���Bx0<�����URh��|�v���w�#+����=w��o�;�����3��yN �^��� ��g���������- @�WOz�)i�r���X�H��I������'�������\KH��w�z�= �<k�H��a8�;]
<]����7�����i�C��.�~��Ln���o||N�H��&���//<Ov��]w��^y�r�!W���r��4o����R��Rff���11��wK}d���G<�����9��'�t�x�����I��;g#������>2�$���s�&��+z@�Y��1���V�k ?�//=��4{��;�t�	��\c��_oz��5�J�0{������������C������r�����do���������wd�P>���T�FN�H�ff#Ep� ����I����T��������H=z�#3���dOf_�Z���&H�
I-[��*UX��<"��3��#}��y<l�����R�F�1B��w3g${��}����y���*���V��"E���x�_����Z�6�OR����4u����f������5$D��gi��sg):���^�6m2�z�����o&�J�~���\R�Z��A�W���r[�>R�JRFF���e�H��#��i���1��0��^o���$"��[������Y�w�b��O7��e�ue�j����p�:r�]�L�y��*W6G�>&X�\�3wd�i�s�)/��6mT.&�����&�0�z�U��)��W��NW���P3O����d�����f>I�b���+`�,�}�5�/oV2DZ�D:s���(8'5����A�|]�������n~�M��ke>��~�RE���3������(����7��v�r�r�
C���)S�c����]�:]
`�� ��k�����7j���)x����G4����1�W��3w�E�paG��R@��?�0��%i�P����J�����YYf	��0�������x�5��o��9��F
z^�!Xp���J�I��Kw��t5�g	���5�|�R�X��������g��%��j�������_��3�����;s��s������`g�<]d����9,K��#ge��K�����k��@��gv�H�z&��!�W	���C�S��f�6y�r�yS�?.��'=j�H�~R�jRf�����f��R����2�t�z ������q��OHaa��x�B�����W^���^��[z�-����"E�����?�z��bb�����;+V�]����5��e���_W^)=����`��,[f���W�\��;i�3��D	�[7�����g��
���>YY�I��_��p��������4z���`A��ZG�����`��^=g�H�f�w�|F�}����l1j��u����*%�}�923MoHb�	$�V�!\�o�s��I-[����b�_@� ���%=��y���T����~/0�LRo�P>\��wi��������y,�%��������%\���$�[g�t���t5��xq��[�aY��m9d�r3���7�$5m�H���w�gLB�=F�6�}���+����s����6+�%%��~�����\��f�l��(�n])6V��K�������+�oP������K��R�N����~��;�x�t������p��������
M�	�C
^v�G�^R���� T�$=�����f%�E��A�������o���{N��Z�dI������3�J~����~��-i@�4d����o��fR�����M�����q�mf^�����z�6���][<�����;]=����`e��q����|[�2�=����Yi����Z�~+%'������V�r�k�=�g���
��u��
�������f�VRR��Z���_}e��\����M0)\����!X(8c����|�YQ����n�]�>]��O��A7�lx$��������7JQQR�6�K/�
L-��������k�Y�S��
s��' @��37l������K=d6=<s&gr{�ZR\�t�}����y%��,�^�23�P�������+RD����%���3Tk���������4��;R��9�
|�F���������-�r�����{L�;���$&JH5jHYY�7�H#FH�I�JIw�)}������ �/�d�O4o.]w����v�
I��I/�,m�*��#������iv_���gO�ti�^=�����NW���o�Io�e3�@A(WN��~���L�X��|����a�Y�E3�������={�� �9 �o��"������������u������������,0sGM@��ssHR�j9K��hazW�"� �;&M�lf�q��J�����YY�7${2��U�?�c�D),������U��}l��<�2e�j�����HEFF*>>^���s_o���\.W����r�b�����j����&�������a��0����2���'�-+�>mzI����W�*T0���m��
= yT�lY�7NU�T�eYz�����sgm��AW�o�����_�=�������;U��N�4�Q���O���i���u3�eI���;�l��#��[�
���6�w�N��@>!����7�������5e��^��@����Dy�{�m���R%��������e���Q�,�{��	!�sGv�0�h-_n~�R��Y������+�p�+���s.Aff�f���S�N)>>�}���?V�%T�fM
:TiiiVi�����������7��M
�:v�^}U��]��'��7�p������?��w7a�Q#i��V��+3��������EHNNV||�N�>�����=[5j��$�y�����+���7k��!��}�.�z���JOOw?O������e��v}�{\�{L�����e�-��w����6��6�shCg���w�t��	��
X�@�����k��k4j�n(\X�v�t��d�mk���k�w�����|V.���,��"���3g���������O?�����e���C��-^�X�[���?��J�*����}�Y�9���3f����#��g���GT��!%�w�v��_N�*��Q]�a�Jn���7*���\�S����z�t�n]�QCY��U
� �����;������t9^�r��i�J�*���������S���������}���������������|�322�����m�*���t}���z��u�:�s��%��W�G�Y��w�8}Z��|SM�W���r}��\�g�
�����v�����T�����|�;���n���T�(Q�r�u���r�?��q�$�t�������


=�|pp���/�����_�$�Pp���\�����m��~��*�:vT�����8.tOfw��/������
��B�!�j%ED8]>�������6�sr� y4t�Pu��A�����'4c�-]�T�������4c�u��Q�����y���������v��N�^p��^��I�<�t5���h���6�eI��9K��\i&�O�l��`�Y���~k�d#D~��G�R�=��~-ZT�k�Vbb���m�_~�E.���u��)����[�n>|��e����1�{L�rs�����1x�t���d�Y�w�<�������1x���;�����W��GS�N�����8-[���j�If�����JM5+�4k&�����'m�`��|����|A�"fY��n2��������%K�}����3G@���qN�H��y�������c�n���se�J�&I]�����,������6Cy�rIU����G�~J+W���[�U����3�7${#���%���O`#B�� �r����!����b����K�?���fa��	3��^zI��EJI��yG��M*ZT:zT�9S��K*]Z�[W:���~�����E!����L���%��9S�������w!�G����m�!����?�T:|���.5h`�o�(�'�liz��t��|S����� � �f�
��_/|���_~1����5��ERP��4	(XAAR����Qf����>���K*YR:yR��3$�B�Z5���y���4���s0����y�e�~�u�t���S �J���w7GV��
��;��7�����4��m�"g2{�j,��q������:�}�7Ks����|2�\���^=�������F��}&=�����.-X`����!�/o�%$H��;]=?E�7�����.�0�+gI�������[��U�F��)Z��d��[��=��m��	f����������������3�)`��	4�����^�%'�������k?�TAV�.�T���������Qi�\�I��f��+�a��>#�KKw�-}����oNW��@�Q��fe���s�/U��?�> ���	����T��=u�Ox�����%�?H�vIS�H�;K��C9��K��6��~���u�����!���2_|1���>���G��C�x����
P�*T�zH��s3wd�R3���k�/���3��6k&�(a��z�]�R"\�?�K�����{�E������&M
�.��BB�jYc�J6H��I��K��nva?~�Ln��~�\9�fMi�@i�B��;\�?����V����k�?:v�����D�������=�O>1C����F�����/��n�^~Yj���N���^�v����.�Y11R����_{A&L0�8���w�%0Pj�H1��3��ofQ�{��bc�?�0��{L��*�re��G�/��N�p�z��sg��/��9w����(��6�DE����N�~���5~�t��Rpp����������>zG�����!p���n2�-�N�4�_}�<���cGGJx0�K�UK4���q�����O�bE)#CZ�D2���X��t�=��c��� 0j�0�X��KIIRj�	 ������?��0�������OfN�k��9"�����9������9RZ���K�/@`���{A�zKz�A��13��|K��O*W���5��5�f
hV���2���gM)Y����MP�� �Q���ob�4s�y��o���p)BC�r�/�(%'��D�}��/R��	(����k��>���u�����G		����3�8$$�^���-+��-��e~����f���
M���M�/�I���fR��)��?;]9��D�g^������:I��~��(AA���J�='}���{d��G34��I���#����U��e��������E"��t{�����[��*_������D	��;���7�A��N3Fj����;r&�GEI���}��mc�_�@�Y�7�������@����u��C�e��U�g��R���Y���'���6���_��33���	r�x�����W�t���?����t1�eI��K���c�2������4+l�p�9��5����[�}]�����p���8�Y3{����\R�jf����fH����y�jf�������R�RL�t�]�G�y&A�g�C��I��������'��$��
����^yE��{�b��oJ7�,)bV���c����R��
0b���n5���F�����Je��>_��9�f�oS���+��`��,[f����k��_��q��l�0�.-u�&������h���c�?��J!]��u�W�0�K�6����x��`��V��fE���5o�2�����j�J��W5j�y#���?�l��!� ����@�eK�J�11R������3G�J�V���f���fI�m�����
��'�W�r�y�����@Y
���~Z��wi����������3�d�ve����7�K���e���8�(^\��sX��ukNY�B���Ln�M�1�u������w�LB��K�YS8PZ��,���WR��R����������OJ�\#��J�zI3g����A�?��$t��
�:u2�$�����h��������7��%5j�3��Q#vD�DW1��res��#��K_�3\+9YZ��#G��]m����X���,��j&��/m�,�����{�m�I��������t��f��:u���M�Iz����!��1��(8e�H��#��?f�o��F�0C�\.R^|Qj�Z���n�Iz�
i�.�+
C�
ZP�Y�7>��:|�Lj��u��4g�9$��H��f�V��f�	�# ��I�8�D	������ezC����_K;w�������{�����f��C��71�`��}�I����#���K=d6=<s���(��%��I��'}��t������O)u�l��v�0����/-Ybvf�:���R�&9!��g
�����3�`��\.�jU�����s�F���R��R��Rf�����R��R�RR�����y%�"��!Xx�B��v��	�m��={�����v5='�K3fH=zH11R���SOI��KNWH"��7z@�n��I��/}��	����Q�����w���R�f���]M`��������z@���R�f�=��JJ2sGM@�=������o���w���*%�u�9��LoH�R��WK�o����0��H�R�U��J�?c�! @j������{-�	$����X���2ge���7�K�|B����+&u�f�2���j-[f�����9����MszG��a�_\>=���2��?���`�Y����Oz�Q�J��YJ�5��cc��=�O>1�J��D�?��E�V�����9$i����-2��?��.��${2{�F�������*V�~X���;�x�4x�T���e���J�F�aZW\!�v���{f�v�<|>�|���j���bcc��k^O�8Q_|����y&���"�j%����i�	��I���T����>k����T��T��4h��9IOw�zx� S�L����cG;vL�����b��i�����I�.El����4s���o��U�3�HM���/�l�^zIj�F���n�Q�<Y��'�+��|:����kz��w4l�0��7h�@���V�cM�x�YD~���^���)-M��+�o_�res��k��<�t���O���Yu��=�|hh�N�:�@E�I���DG��Y��I��I7��[�Z�]������x���M�[����a���r-[�2����l����.�l>@*T���7�s~����^����#���2x���~�������R�
��3f���Afr{��f��Y�����5������L����m����yx4�^'m������N�>-��������O>���c����:]�����P��t�M��,��svb_�������9�������7���[��Yf�^s��O��]�����|:��w�}*T������4�y�������I�t���;]��`p��e6<�R�l~x���bEN ��M��s�a&������jY�����:w6�R�q|:�HR�����{w�������*Y���%�B����m�����/��l���d�"�;�e���R��������s@~��g���S��;w�������C0	x��8���������2�y����K���W�^���o�9�f�������p������Q�t���K��d��
j���9��4ir��������i����u�.��5i�����g>@\.�N�8q������wE���G`�4i�y���a��O��t���y��;vl�������c�����s�2\��]���2er�/[�%x��O����/�y���Z�����n��JMM������0x��]���uv�m�7O�t���V����>�R�F
m��Y��v�:�'N�G����T�fM�����(0PV������-^��{@$)66Vc��q��Dl�����c���ou��!eee����G���0���&>@����������������O?h�\.`3����O��{����'u��1���������N��<�`�f>@�����{L���N������ti�����[�t�������t��I�
��m�T�V-��~�M79T���6��r���K��{��s��\�\;�(x>@���.��!X��O�����O;]��bl��$33S�F�R�2e�]�vI��~�iM�:���<= ��O���Gk���?~�BBB��k���w�}���<= ��O�>�@o����w����@��:u���~����2e�j�����HEFF*>>^���s_?}��������hEDD�[�n:x�`�}-�/���w�^U�\���YYY������*[��������k��u������sgm��U�����3G�f���e��o�>u��5_���,���W��Q��V�X�+��2��O?�Tu��������s==z��L����W�l���:u�f������^�4m�4U�^]�W�V�&M.�)h��M|:��1B={����{������m��]|������K~���L��5K�N�R||���_����i��}O�j�T�\9�Z���$==]��������������������
���������?'v|^P0hC�F�y?������|V.�O���;k��9z���T�pa�1B�����9s��m��~���d��������������U�F
m��Q!!!*V�X��K�*�\������#G�s~��
���.V�T
I{�����s��Pp����.��6�n����
��]m���f���2�
 g����1ct������j����q��?�O?�T={���e�.���������*..N���Sddd~�����%Ie��Ql���~�JJJR��m�t9���w���m���n��+�t>@���4~�x���#�^3$$�=��~��Z�v�&M�����:s���;���������������*44���������0k(�o�^���
m��h?�Gz?��������U�Z�n}Y=�$++K����_������h�"�����+%%E�������I������HR����O*99Y���W���s]������ZC�U�T�\9�8qB3f����K�����E��w��0`�����G}T�����`#� �<��$i��	�\s�\�����k:tH=z�����U�hQ��][������������[7����}��z��7��)(���t�������:u��^����5y��|{O�06��9 v��i�K�<���f>@2335j�(�)SF��k�$������
b���t=z��O�����+$$�}�f��z��w��O>@>����������������~����<C�`3� {��uo�gYYY���p�"�,���H�5�b��s�����[��yz@`3�^�w������������,%$$h��������W_9]���6�����;k��9Z�p�
.�#F������9s����������z�����]w�uJJJr�,��,���z@���TIR�
��o�9\�`l�s= ���������cGY��_������+W���<= ���������GU��}�r���a�s��,K.�K���T����M|.�<�����;�g���][.Ttt��e���I�5k���i��B�
9]�gbl�������^�8q����C�`��a�E�6���$t�s�\a�%`l�sD��)�����iS���:]�gbl��$[��=�.�;���\�����;T�D	/^\��������6V�����|.����+*R����������aW�z�r�oBH�M|.������������w��r�b��j���"##�.�306����G�o���]��-ZTo������;T��6	p�����w���{�Q�.]�a����JKK��u�t��7������M��.�y���f>���k��K�.�>}z������|���4M�4I����3~�'{@���k=������Ci���6V�����d��o�����^�����w�^+�P���|2����),,���CCCu��i+�p���&>9D�U�h��^;v����������;��!X��O���,�K�.�1��'�� ���� ��!��
��16#��!X����c����w����Cu��QI�w�}�N��|r�l�7oV�6mT�hQ���[������������}��N����0`�z����;w*,,�}�c��Z�|���������k����s�L�2:p��y&��f>@BCC���z��;v��+�p�"�$t����M7����{N�$�����
2D��us�:@l�����_���'U�dI���j���*W��"E�h���N���^�h��JJJ���+�y�f�<yR���S�6m�.��06��������u�]�t��!X��O�W_}���].����T�re5o�\���6W�a��M|:����+�����������K�~��w���+""B�R���d����9\����|z��1c��aC���SG���#G�c�5n�X�&MRJJ�bbb���K��O��>\�}��*U��>W�re���K����v�������$/C�`�������={���g��wB�����'�.�306����U+=�����a����
��������$%''�B�
N����M|:�L�:UQQQ�_��BCC�
(**JS�N�$EDD���_v�R��������%%%��~��;$IU�VU��U���j�������$[�j�T�Z5���\��M|>��������/����3g���6a�������|:�,Z�H7�t�*V��~�A5k�����eY�����ty����I�C����������0}��g���_��E�z��N�����`�&>@���{���C��?��Cz�����/8\�|:�.\�=��t��������>�TY�������4i�D+W�T�����cG=��JNNVBB��4i�ty�c:l��d��	:y��$i���:y�������J�*��8��H����.�7�|��j<��`3��R�bE9r������N����������y����t������<= ��O����/��U�hQ����L-Z�H���w�2�$t��'H�.]$I.�K={��u-88X�����/��@e������%I�P����]�%J8\��cl��$��?��t	��!X��OIZ�h�-Z�C��{F����{U�a��M|:��9R�=��4h���K�������ty��75}�t�}��N�@>���3gt���:]���g6��r�}�i��N����������O�������U�vm��>a��<����c����~�A�
���^�^xAU�Vu���eK-[�,��{�����o^�R���M|:�l��Y�\s�$i��-��]���e���O�>j�����=���zJ�����m�T�pa�}����{�9�����K�
�������%K������������U�dI�_�^��7w�WLLL��/�K|zH��Q������?$IV>�����������\�?��c�(QB5k����C���v��U����t��#Gt�m�i��%r�\��s�*V����{�x��z���/�u�����_?5m�T5k�t����;u��W*66V�7o��!C�}�v%$$��u�������~���*I���PFF�%�v1\��
�����,��/�sb���6�n����
���m�g�������P=z���C������z����i�*V����D
0@[�n���}���5o�<�\�Re����}�/V������?�R�J�\��g5r��s���1���#u&OV��$}���v�zk����KKK��w�����+22��r��O��,X�@������*U�h��=���}���W_}�����m�����K����C5`�����T����]�v�|�]�gK�*U���;��!�edd())Im��=g�7x����~��6�~v�a��\:� �N�:oO���GzQ�eY�}�Q��=[K�.U�
���l��Q�T�t��^

=o������
0S��7]�f�g�6�n����
��]m������$�f����>p?w�\���������U��z�>}����>��3T�H8p@pOl����4j�(�_�^�w���_~�=z�y���]�v�~]��I���O���?^�[���u�t��
<X[�n���G���__�kM�2E��l���M��^�z)$$D.���u��)����[�n>|x~}9��w���C�t�Y��v����_]E����'��kW�������.���������:��|:�HR��E5l�0���L���!X��O��6m�f��u��Y�f����w�"���t;v�J�(q���%Kj��1T����M|:�����w��+��R)))T�a����t)Y��6o�|��M�6)::��������;��c�=�%K�(33S���Z�x��q�~��N��<&��f>�
��Q��{�n�n�ZAA�K���R�=�8�g�eY:p���O����^7nT�B�T�V-]y��N��Y��M|:�T�\Y[�nU�*UT�J�K�<LB��|vH@@��T��#G�8]
����"I�����A��e��K�LLB��|v�$���Ciii�S��BBBT�P�\��=�Pe���2q�D�K����&>@z���t	��I���O����~�I���w��C�I������[�:\�|:�,[�L�j���5k�����'OJ�6m��g�y���<��`3� O>����y%%%)$$�}����������O>@���u��7�s�d��:|�����H�b����s�o��Ae��q�"�,��������!C����r�\�����_���G�N��� c��Q�j���'O�F�j������Z
>���<= ��O��w�yG#F�Prr�N�<��u��J�*N���6������_|Q_~����9���[��g�Q�B��.
�k>9k���z����2e�h��I�����ey&��f>@>�����JLL����9s����?VVV���~�'HJJ�:v��~��M�\.������<= ��O��g�*,,,����`edd8T��b:l����-�R�^��>w��i=��C*\���\BB��~�'H��=�9w�]w9P��c:l��d��iN��<|r.= �	��1	6#��
��1	6#��
�m ��!X��m �����6�!��3�`�f�!��3z@`3�@�����7�blB`�?c:lF`�@�C�`3�@@lC�g�C��@���`�6�!��
���lF`�?�6#��
�m ����`3�@���`3�@������@������� lC`�?clF`H�;V
6T�"ET�dIu��E��o�u������OEGG+""B��u����8��� y�l�2���G�W�VRR�222��];�:u�}O���5g���5K��-��}���kW�<K��x�����z>}�t�,YR���W���u��qM�:U3f����_/I�6m��W����W�I�&N�
x�%:~��$)**J��~�zedd�M�6�{�U��r��i��U�
 ���JOOw?OMM�$edd(##� ��$dfJ�2��d��~���;>/(��w���m���nC>+��r	�����_?5m�T5k��$8p@!!!*V�X�{K�*���u����#G�s~��
�������Gu����d��;���'))��p�hC�F�y?������iii���/#�\�>}�h��-Z�r�e����C5`�����T����]�v�����2�Q��	��Z�j�N���~�JJJR��m�t9���w���m���n��+�t����o_}��WZ�|���-�>�3g����c�zA<������Vhh�BCC�9l�_���A`P�����������������~�����
��\>V��#����o_��=[�/V�
r]�_������h�"�����+%%E���v�x$z@��O�>�1c����)R�=��h��*T���-���{k�����Rdd�}�Q���{�
X��@�h��)���-[�:?m�4���K���+�(  @��uSzz���o�7�x��J�E�#+���o���i����<y�
��<|M@~b��@�������@���6�,���6FlF`�@�C�`3�@�Yv`��@�������@�������@�,���6�$t���6�$t���`�6�!��3&��f�!��3&��f0�!��
��1	6#��
������@�,���6�,���`�6�!��3��� lC�gLB�� `lC`�?c:lF`�?c:lFC�`�@���`3�@���`3��@�������@�������@�,���6�,���`�6FlF= �
�m ��!X���m ����z@`�@���`�6��)��I�\6��@#�����|y����$��/�/o���o�[n�~�5���{�yB
��dfJ�?~��?�����p,�?Y�����?�,��_�}@ ������u��7*66V.�K��y���z�����u�p�
�{!����}�E"����S�T�NM�<����p�
�������Ol�0J�������t��C�����������*���Ie��	����r�����_�= �h���*Y���V���~XG�q����I����n>��|�DsP��'7�p��v��
*���~�SO=�:h��U
������JOOw?OMM�$edd(##�`
��F�f�T��r���>m�)���_�u��RA�7�]����>/(p��w���m���nC>+��eY�����r�4{�lu��������K�*U�����u�������j������1c����������T��m
��w�.^\Gj���������;������t9^�r	�@$��+����?�|��������������PRR���m����?�?���������~�����
SSSU�D	�e`V���_u������BCCz����`[�	��~�����
����hC�gW�9�|�<:y��~��G����Y7nTTT����4r�Hu��M111����4x�`U�\Y���w�j��@�h��uj������$I={���)S�y�f����:v��bcc��];�5��=��"��Q��-�w�em��N��6�!��
�m lC`�@���6���A�wZOMM���222������T����_����
����hC�gwf����s.���8qB��p%�;'N�P��E�.�+�,��������}�T�H�\����T�����_~Qddd���m��hC�F�y?�������e������U@�.= $  @e����}###����hC�Gz7��������6��������6?�g�yF���N��KDz?����~��6�~���a:���6�!��
�m ~l���*_�������qc}���N�������
�H�"*Y���t�������������������n��������IIIQ�N���%Kj��A:{�l�{�.]�z��)44T�+W����������7n�\.�����>G�y��{�����Rtt�
*�Z�ji��u���ei��*]��
*�6m�h����^��������"##U�X1���['O��u������Y3���)..N���?��Y�f�Z�j
S�Z�4w�����}Hff��~�iU�PA�
R�J�4j�(�yM���,_�\7�x�bcc�r��������I���Z�,���3gZ!!!�{��gm��������b��Yt�4���}{k��i��-[��7Z;v���+g�<y�}�C=d���Y�-���[g5i�����k����=k��Y�j����a�k���V�%��C�����k�n
0���m���k�Y�����������<�~��U�|y�v�����?�>O�y��G�ZW^y���W/k��5��]����D��t�3n�8�h�����nm��������
*X�����n���S���z�jk��V����;���}����V�R�����[[�l�>���P�B�[o������������[��m��n[�������R�G���������������Y�fY��I������e�����a����K�5{��\�=���R.�O5j�������yff�k�;������C�,I��e�,���c��Y�����Y���|����$k��U�e�o�����L�2��������-�����[W_}u������m�o�������;q��U�J+))�j���;��~�o��!�u�]w��YYYVLL����/��;v�


�>����,k��m�$k����{���g�\.k����eY�o�a/^�����]�jU���n�����S��o������^���:u�d�{����u�������eY����k����K-���Cg��������M�����i�F�V�r�2�s��qIRTT�$i��������6��US�r��m�j�*��UK�J�r���}{���j����{����d������Ou������������/��A�z��*Y������w�y�}�����r��-Z��7n��
�+�
��i����f��=��7WHH������k����������];�����Z-Z�H;v��$m��I+W�T�$������+/� @���������� I*U��8�PU�'++K���S��MU�fMI���b������ms�����]����'55U�����0s�L}��w;v�9�h?��k�.M�2EU�TQbb�~�a=��cz���%������=p��J�,��zPP�������i�����O���oW�j���u��_�~����$���xR{���� ��U�>}�e��\���R�G�����q%%%),,��rp	�����A�3F�T�n]m��Eo���z���pu����������5c�]}����q�������X������%J(00���y<������/}���W_}�%K��l����111:s���;���?�MLL�y�.�����B�
��D�����C�T�^=)((H��-������� �*U���p�K�V�5r��^��RRR$�����������C�r]?{���=�/�L��A��{Aj������[���w�J������+/� @�PHH�����E���eeei��E���w�2�gY�������gk����P�B�����Wppp����}�RRR�m����\��������*>>>�kd���|.M��������7��
�{������gk���9K_���CW^y�$�B�
������655Uk�������������,^�XYYYj��������+##�}ORR��V���������v�����)  ��/�������DzOj����|��,x8c���Vhh�5}�tk��m�<`+V,��<�?��U�hQk��������GZZ����z�*W���x�bk��uV||������k�v���7Z���������.�:h� �����&O�|�e\�\�?��eY������o��� k������;��?��
�>��#�=�����+f}���������;�wI��u�Zk���V�\iU�R%������J�*e�}����-[��3gZ����,	d���K���o=��3,��={����)�^�7!!�*Q��5x�`�=��g9q���a�k��
�$k��	��
�={�X��Y���Zp� ~���^���+g���X�5�V�^�tI>O�y�i�������?�Gy�*^��n�|������s������:X�
�J�(a=��VFFF�{�,Yb]s�5VHH�U�b�\�������k��<��9s��5kZ���V�j����~;����,�����J�*e���Z�[���o����#G�Xw�q�aEFFZ��s�u���\�l����������P�L�2��q��������u�UWY!!!��W_m����_��>&55�z����r��YaaaV���a���Z~�6�,K�,9��}={��,���+/����,�O[�@b�@���6�!��
�m lC��l�R���s�-]�T.�K��s�@>#��)�`�@�������L�2*\��7n��K���O�>]��Sbb��W�����p�
������g����S�b��!C��g������$�W�^Z�l�&M�$��%�����w��������A�����k�����m���8��}�j��U�9s�6o��[o�U7�p�v����'--M/���>��C-_�\)))8p���/���?�X��M��_���T}������&MR||����~���_���W\\����a����/k��u


����k��(8AN�<)))�6m�RRR+I8p�����i��i��1���������T��$Z�{�9�����k:t�n��fI������s���-ZT!!!
WLL�9u�=Z-Z��$=�������N�>��������8Grr�233u�UW�:������h����pw�����K���C���������j����z``��������<�Q�v�\�-I�R�r�.��x�'O�T``���_����\�"""�����s]s�\�,+������\.I�sxx&���Q�n]eff���C�\�r��|C���h��*U����]�>�������.�}!!!���������9���*u��]=z���/���u����~��E�T�vmu��)O�����j����\���U���^{M�����7C���/�5k�h������PTTTA}Y@���M��=z��'�P��U��K�]����_2Dw�q�z�����xEDD�}���&�8P����Q�����
������.+?��7���T�zu�v�m5j����,@���g�,X�-Z(==]����~��g�y��N�pC�&  @��OW��
��iS%''k����^�����,���m lC`�@���6�!��
�m lC`�@���6�!��
�m lC`�@���6�g��G�fW�IEND�B`�
int_sort_bench.pngimage/png; name=int_sort_bench.pngDownload
int_sort_bench2.pngimage/png; name=int_sort_bench2.pngDownload
int_sort_bench5.pngimage/png; name=int_sort_bench5.pngDownload
�PNG


IHDR�X��%9tEXtSoftwareMatplotlib version3.9.1, https://matplotlib.org/��!	pHYsaa�?�iyIDATx���yxT������$!D$d�Q�Gee��>(��(��K��T0E�� n("��-u�Z7��W�Ej�� ���a	
b����8N��,�Lf�������9���z
��������l6������@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0u����0����z(N���#�0�p��P��A@ �t��0W^^��/�\)))����f�����*�s^|�E
2D���Wjj�~��_h����������N����o�3F�������������J��{�N>�d%%%)##C�����k���O?������v����3����~����t����_���*��?_\p�~����W��U��>���N���N:I�-��Y��n�:�s�9:|����~�����0`�{�1�r�)��������6�c�Zu�Ei��y2d�{�1��9SG���_~�p��_~Y�>��~��_����={���������o@4���g}����o�-I��������?���={�:u���s�����~���k���5h� ��p8��w���^��W_-I���k��W/=��s?~�$��^��u��h�"�r�-
���3G6��al%%%��c��t�"I����&L����W���.
�� D)f�s7�t������o$I+W�TQQ����u��������Ozz�N:�$�����v��AW]uU�����6l������co��������)�0��<yrC8����\Ir��=�}���������gO����z���X�z��>�lu��Q��u���^�={��}��N:�$��������8���G;v���f�I'��n��9���m�8�pmVV�S�����~�����w���~��)>���v999N���p?��������k�����k������w���	&���@/����9�[n�E�������0b��8�]__/�0�����b�8���C��]�#�i����}?b=��?�a��+������;��+�����:�����#�(//O��y�fY�V=����3c��=[&LPmm���m@���c���������;U__���{�b��f��O�>:�����~}�������K����3�i�&����������.�L���v��!I2d�����t�RY�V9rD��_4v�X~�?z����~��'$��h�����b��y��f�m6�:������KU^^�'�|��5f�f��RII��.]������S�9;�j�*-]�T=������5k����/���kY�V
>\+W��� ����[�\r��?�|m��I/������

0@�����;���={4q�Du��Q�w���o��_��W�={v��o���z��TPP�O>�D���:~���{�=�x���0aB �Mb.}���Z�N�%�����kWI��}�t���k��i�2e��=�{��G�&M���k��|�|�r�s�=�3g����5c�=���
���3G'�|��q��7O�������;O�\rI���b�h���z�����/��7�P��]5j�(�~��~���#��Z5�,8���oj��������J�����S�:(==]w�}�V�Z�O?����o��V�����i��>��`~��A�K�
��j��z�6WYY�P������>F�&��a�����[�u�VI���[����D'�|����JM�:UEEE��{�>����?_����$��/���~����O;v���������W�^4hP�3�<,q�a���3f���i��i��e����<�^xA���JKK��g��y��5�A|��W�`�}���JNN������#���SN	����@`�;a��@��{�����w�}��;���a6�MG�U��=��3!4�1����Svvv�� L���WYYY�D@�9;v�d��0%%%�c�����5kt�y�)!!!�cAd���/xn����g��sSQQ��������#�������������JII�.�
�|�s_���W<;�E�����6��:a�=�p�j����6��Q[[���xUUU�j����UBB�,K����p`���o�>>|8(������{�R��
:w����t�7"���{��JNNh������c���C������l6�*++u��IRFFF�G�-�h`�Z�y��]�~������QRR�G����$8p@��wg�;�HEh`�s������5���]3@`��������^@t ���z���2�ce�e���s��A4G@��Y�Rq���+�GZ������Y�f�z�~UT$��-�#]q���wo�8����z�~ST$M�$}�����R�x�Bzuu�n��fu��]III5j�>��S��,[�L999JNN���s:t���555�1c�222����^�zi���
����h��	����RRRt���k���
��{��8p��}�Y���GIII��/�>�@�/�a2C{��i��_�4EK���l6������V�����]��0��3��c%{���z��q���m����k�q�m���7�����g���K,��q��s�N���:������k�����5q�D�Z�Js������������U����rrr�w�^��������!�������M7����'�����;w��o����"Y,���K_��N;�4�w�}��n��y�M ��?7�����/���C7.�:<���:th�}l6�/�N����������I'���}�?�?���Z�l���/Iz��g�v�Z=��s���[��Y�x��?�|�v�m���O>Y7n��U���OII�N:�$�5J�a�W�^
��[�N_|��v�����lI�/���������~�3�<S�9��/8����D%''+==��o�����d�}����t�X����k�.���j���
�4l�0m����5��m�Yg��pl����������n��~�����o��5k������%��SOU������W/f�b��j���[i*I�f��&:<JN6g�[��r�w�[������z}��aUT�;�/99��Wk
<X�w�������Q�_~�&M���{����D����k45e�I{���txd�R����w������q������������������D}��G
�jkk������SOuyM������;��?���{���h���z��g�|�r����������=�����[�v;���DY�u)@�*+��y�n�A�_X,f��I��p�t	�=l6���N8A�����[o�U��������TYY�k����57�|�F���j��	Z�z������h�"eddh��A����������tu��Yc�������+��R��������7����Gk������wo}�����g�:t����T�5����������H���|��Ef�������x���u������������s�N�^�Z]�tqy��g��g�yF�/���f��u�]��c��Z�`���3�<S{�����+'�0���o�K�.:��s4v�X�x��Z�|y�c�={�,�N=�Tu��M%%%>�o����k�<��}�inn����e�l�� ZUTT�S�N:r��RRR^��������t��j5������	��u=s^__���
���0k����Ijkk�r�J]p�JHH�p!xn����gMI�^�|����>����S6@h��~g�Hyy�N;����,s(-�`G@�z����J�g������R]--[f~��_����������r������
�������^+��~- �@��!���f��k�
�h	����m�������rr�;FD&:x��H�9S����cYY����������K��������%����"i�$�p.I�������co�m���S����������e���s���d.k�9S���_��5K��������3�M�Nq8x�G~Sr�D���n_OKNSN'6�x���6�C�j��#�[��y��)�M���\�~�`�����v\���:���H��=�OC���O�'���H���;//O7�|�n��6���*==]��{��9��u�]�n��)%%E?������S�t��Y,}��g����z�������n���_Tvv��1�X�B��~���o��]�j���:~�x�����>eee�]�v8p�V�Z�p��={d��/_���G+))I/����O��#G��0���=�7EER����1�W�{�v\��\Y�w�n�%i�>����;t�Eye����<�SUW�q��-���?��N����������k^����t��������y����������N�:i���*..�$}��2C[�l��c�$I|��F�������4e�]s�5��m�������/�Ok�/^��{L.����/�7N�\r�v���p�9s�h�����m������B������Leee�={v���-��G�TF�o��t�����=;���f��x����X��W����G�kk]���js�g�q������N:IS�N���C�n�:I��
��'����_���Cu�I'i�������V�X!�������b�{������6l��p�S@���S~~�z����O?]7�x�:t� IZ�p�n��v���P�~���#�h���*,,t���Y�����>}��W�^�����Pzz������x���U�����u��}�������z�������Ju���`8j�(��;v�1��x���=��3������$I���?u��1u������Q�v��$�=Z�=���V�>���w�yJOOWqq��8����Syyy.�{�������~��7n��;�<M�4I]�tQEE����;�9����#G6,��:t���/Z��}���K����������������
			_����zI��c�����0C���J�9����G����?��~��zH���z���5`����S'�t����X,Z�v�6n��5k���'���w���?������p����@�y�]������#�p������w��u�����_��#v��QrB���q������������40}�$�xZ��
�tLQ\��n���6�z����k��}���W���]���sg�q�z��'����SN9E��w�����������v;�04r�H�9R��s�z���7�|S����>��#�{|��G6l��{&&&��F%�5�����w�5��Iw�e~��#�
78�w��o_s�����aVw��m����G@�G�ax���}B{���>�}�����eM������?�;V������`��|��������;���?�y�����<=���4i�$)55U���������SO�����u������S��������������$��[o���s��o_
8PK�.���[��K/yw���u��1�[�NPrr�����
 ��k�VTd�7o������={:����~�A:�t���6��|	���f�9�p��a~,,���
����ah������;5}�t<xP���:��s��G���F����B���yyy��?��v��$�����?Taa�***��W/=��c?~�$���o��#G����V���������n����1B7�p�&O��C�i����Zh�U����L�.t��>t����A�.!A�����O3�;&-Yb���R����/�X�z<���A�w[k�e#�UTT�S�N:r��RRR^���������O%%%����>��Z�%�'i������#��%�JIq^�����[�������+u�8�����/xn�+�����K�%�t�*�o��32��J���];������#���x�nF��~n<e�3����N9�>c��>�i�i
����vi�8tHz�=387
��I����%i�F��-���Z�x������C@���.��K���V����|��Y��	�%G���@D�G?����[�#f�D���w���-=�a@D��5C����;�_��������_-!��	��#��@��X����~2����J����ee���\zk)�����<��
��*++C<����-\@,?^�����=�/X ��#�����������Wa��~�u3��?�
,�:w��H����e��n�
���USS���*����f����RP���e�_C�-���rr���7��7�;���g��f��9��`\V��[j�����%�!���f��?��������s��
��b��*-\h~>{��?���{�������w���\����a(##C��wWmmm@����V~���9��g�(!!��ss���o��RS�k����[;��.Y,��?�����:%%%���Mz�Q��3�N�xb�/�������JIIf@�	3�B�j�n��������n��;F ��B���u������m���������m�

B7^ P����,��oK�&�{��*-5���-���cxOJ���/���+���� �\��gfJUU��\j<f/���?��}�
z�#�P$@@���i8��Y�C�|���Y��<-��j5g�]�����&��k.��@��_�<s�Oee��7lt������D�8�mf+�����f���'K���
C����MegK��j�n��HRa��{��t~���yj�t�=���
��X�=h?��4a�s�t�E:�l��lYYf8���
3�!������/V��=e��z���)..������];�����-[�q�(*�z�������B:�|��O�N��x��{�TVVc/s�E����L1?�g����={����^~���{7����:~���k��F�^�
�{�n]x�������K/i��u��������q��a��k�^����?.��imW��-��w ��Ch���?~���/Y�D}���c�=&I����6l�������h����%���
C�5�\�N��#�G�M�6i�������Y�f�����Z���
_WTTH�jkkU[[�qz����"�|�s_���W���������JK��ef�t�����[�S�6��w����u=�M�G���h{>�=����O=z�p8��GUTT��T������?�����t|��5JNN�X[c����"�|�s_���W���l���G9��xi�t�}���z���:~���C�:�zn*++��>�=��q�*((h����B���:�����������[�v��=�\%$$�t,�,<;��
|�s_E��c�J7�d��z�9}����5z���+������E� �G���t�������������r�\���k�v��9OHH�,�i,�,<;��
|�s_E����G�L�����>fL<}����&��hC��2|�p�[������k5|��������s
������Ulb=��;��[�j�����6j[�nUII�$sy���S������7����n�W_}�?��z���t�-��b����7o��^�<c�{}��g3fL�������M��e�TVV��%�O�>z��wt�-�h���������>K�5Tn����t�����K������K�s�������]�HI��-sy��-[8*�����SR�c�\��j	;����4(*�f������X\�T_/�r�t��c���,3���h;:If8�4Ij���������IW\�v P�d��3��v`�����+X�
U��(e�J���+���V���_����9�M���<@`0�D!W{������]����ykz�hf��(c�K�|F���<^T�|�����=@�1�DO{�m6s/��Y�EI76{��4���[o�u����1��Do��geI6o�
�1���u��X�Do��7
�Rc8��s6���,i�
z���:E��G�0�w��v�r\�N�s 8�@��5g�=-sw���}�Fz���w �X,������RB��D�������nv��yw=����`�;��V�:��{�}��8k�t�%���!��k�Cw���Vj@h��0VTd�5o��<+KZ��uU�O?�>�@���n�E��v|}�bi�$Z���%�@�Z��b��W��V��'Mr.�VZj/*r������)S���d��+h��#f��s5K��)UU�^�n��3��fI&4�x���%i�l����o^��e�����}��y/-�|��%����-�\�������8������pC@B�j5g�]��{���\����3���<��_t D��w�_�Z��"<��uB�t�H��	 4�@�4o���2vo4
��T[+]v��HD@��U!��]�~���Y�]��C�<_gf���r<�������9�A��j�>����f���s�w�����0?>��sK��R���ZKY�����#�~���=^�P-Z$��I��-_�����<+K*,l�G��%Zq�w�����<������~���s��a���8K���~�xn��KK]����znn���E@����33g�=�X����_�[_XH�8 �P$h���37);������f+��L��YY�X"3�@�Z���������<-���@y[��[7���|a�x�4@x#�md/��n���h�������vp�����"]}�4��kM��'&2�
�=��~��'���S�
���A��_����3g���/i��:���V�?Pc�����W�@-^l~��������l:~�T�G ��K��68p@z�%��Y�B:�t��������T]-
&
�q�.t���"i�L��iYY�2���������2?�5��b;��%��O���I�����������/_.��/ef��@[����g��l6����f��jj��b������7���!%$k��K��<�%o����<s���&��k.w?x������}bQ��^r����T���<�K���R���{��5��:��������J�^�����o�{��%M����;��=��*��%�7�����s������~�����TV����*�����w���u���A@T�GH~�5s/�����R���vW��
�,@��������*��R���w���Z{���s.��x�Yx�0C�a�)�mX���b�H����5{�n�y�����|i�
)3��xV�y�k��tD�5k������[7���YYf�\�H/,�����Vj��7.��e���5����������E���i_���b.�#� �Y��A��W���O���<�i�6�pC@@�**r^�.I��I'��1���{Mg�����~��������y���S�
@d!� ���%�����\�s�Y��}�p)h���UTd��7_�^Z*:��:�M����u�HA@@X�Z��sO��-)+��x ��K��;���VF����t����~���e��HA@@Xh^��K��c���B
��,t���J�		��1)5Uj�����,3��b
@�!� ��������m��0_���?���J�y�tf�D"����SO=���{+))Ig�u�>�����_�~j������u�-����*H����*����������,i�
s��b����)S���s���Z�|�


�d��u�Y*,,��q��}�vu������_~Ys�����?�#F�����/�K��E���;h��*��lf����3�7���=�-Z����^��O�$-Y�D������y��3����7j������+$I�{���)S���u��j^�����0g� ���=Djjj�y�f�;��X\\����M�6��f����ys�2�o��F+W��\�1x�j����W^1?Z����"�woi���+���]��=�g 0�"����Z�������=z����ry�W\���r�5J6�Muuu����������TWW��������
IRmm�j�V^	���z�<<;��
|�s���*m�`4���ekXn����

,*-5����i��z=�x�O{�_k,�cs8ng6efJg�]�H����_����?�R\\��zH��t�Ygi����9s����~�}��.��?�����t|��5JNN����v��P�g����/xnm���g�=]��o8�������/$I�<r��5����E����!������q�M���O�zu�?�t<;�E���������g�l�jf"�jjj����+Vh���
��M��������v�&77Wg�}�}���c/���~��_���c��s���j=;;[���JII��7�J���Z�v��=�\%���h�g����/xn����_���4nf�NM�������HK�������,�{����<�~\���/���TTT(--MG�	y6���ILL��!C�n����^__�u��i��.����t
�������=K�v���];��			a��E8���g����/xn�e�����vh6���p���BC��M+��X"��U��"X�
�f�����"\AA��M���C�j��a*,,������O�:U����?�$���/��E�4h���%�w�}�.�����-�C���L��9�*zM�<Y�=���}��i���Z�jUC���������K�a����Rii��u���/�X>�`��#���0��,��9�2z���1������b�����5w�\��;7#h��6g���L��i�ya���@��>�hQn�9�m���fR���������z������,i�
)??0��HB@@�,i�b�E���������.�/X ��#����������	�`�wxe�x�sg��a��YY�u{��0�,*�X��q���B!8p���<�������������D_����*������;��sC;�F�A@��^��;s9����
D':<�����7?�3Gj�.���h�w�e��/����e���J={J�^�a��H��+��������N9A� I3g���.+�l�����������|���������T%GJ���~���r{NR|�������� ����c���I��{�����g��^y�1���I��w�@4(�,��%���J���t�b��j��7�R��Gu~��^��Js����9�F���o+��� R�b����3��5k�4a����@��f;�
:@k^����{�l�������<�
�x�,c�A@�R�
�u��������gt�(���?�����B�b�9��e<�k�0Z>���YYf�t ����@@�p���[���KK�l�:;[��/������w�0?R ����d��*�'�')-9- �Fd!�D0W��SS���������`��k����v�gV��i��h���_T���N������)G���{oD:@�r�������>3�u5��|��Z�Yy{x�U0*��O������lt���>sW��Kn��J
�)T��X�o�"�����3o����e���������E���������e,cG�#�D o{����9S�^rt�4r���,UV�~���</�@���>sD*:@�h����lw�������=�s�}��n�Bx��2�/�W��& ��n���>s� ��g��%-^l~��5��s;=�K�Q�����$����0t�s����T��R���)-X@�r��@{�;B��B�������0�Y��	�Y���*q�@,����d��{�{�
�!��A�#O��\����'��kX��pD@�O���l����'��*����*0�������
�M�$�X�:���J]�J���=���ss}3j�TcO�$�hr�2:4�&j����tt�6j��[�bn���WWKqn�F�=�~n�Z��9"�7��k�5��������3G�"��QK���sk^m�������4�];s�����\r�t�^�����o+���t5�n�>s�+:@y[���y���=�%����K.q_`n�z�#2y���-(��hB@p�j�>����f����>{[�-#����w�I�=&��J��K'�����3�X�u����2�����	���j����j�"�j��[�
.u�(=����������W;/�����=���E@h��j��gK����[
��TS#]s���f�����O��������K���:�f�?�������=;���~�m���qW���(D:�OZ����;�]������_�9�{��D�@�b�'�Vcwg�^3�7��Vy"�?
�5��^v�L���Uc�q{
��m�?���'�Bvk����n��] [������7;B6 �?��5���e����m�oi�����a������m��2�DK��&)������}��f�s:��S�?�X�/�����_�)d[,f��I�����t{{��B
�!��Y�^c��E/_��{4_�n�L8`"��d��/1?��I:r����l���.4�nm����V���Wo<��e^G�5�W��4"���e������������N������V��>��N���U����1��X����=d�������i����9��vh@�"����4�7
�EE�A[����?�YJH�F�����R�=�!D�5d[,�RC��
ot�\���,i�s����m������K��!d#R����h�����RQ�Y��y��[��G�_g��Y�,9��qWN��
:�:V�9s�j��%6��w���}�H��
h
_���j�9E���!����~}�z�K��r ���/����3�;�t�������?�uFF����7�����T^Y�������R��ce�_��k��k�c�D���k�0���J���(����c����;~������G@�]�4Wrs��=����|O�p��n���B��tD����
���t�!�����H��[3F��
�c���qW���79�0�?��*ef:���%�Xa�:bK���a�5w��JK����� ���,x������ee�3�����������]K4���C@a�S�4���g�EI7�A��Q��[�s.�~��!�b���������h@�!����|�����]��gyV�t���kg�i�{� �#2Y���� #���PTd���<5��k��sI��3��7�O����Wo���3����}�������%M���r�^���-����I�2:4����o�`�I�o���~9���l+��U�v�����B��>���/_��%���#%��d?U�U�t�3[�����}
��3"��������UYY��
4�t����N�*wi��-�qx���\����T^Y����������,c"���:5�q�{�n���7����9@[��%�:0���)G�glw�l^b;��!��SO��G��}�4`�=��6l���>�;��SEEE������W/��.���o�k��{���G����5���Zofu��\�����^q�}�-�,o�,�/r:���(C@�������@K�,�Yg����B�7N��oW����������������k��������u��9���O����Z����v^�s������r���
��XXH�8x����I�I�>c;�@@��z�l��E����5}�t�z��Z�d���������<����������zK#G�T���5z�h
0 �#�?l6�����=X������+��L��YY�qZ��5�����@ ��`��]����4e�8p@������������GMM�6o���c�6������c�i�&���������u�M7�G�:�����C�j��� ��V��Xz�3����/���[��������_z�e�����s@�a�{+}��?~�F��?�P>���w�����z����b�
��S^^.���=z8�������+��|��7�����+��R+W����;u��7���Vs��uyMuu���������$������Z� ��������_����7�4TP`Qi��T�/i�w�����
��}��F�d��_�������������&����y}^�g��k}��=}��w�T�!����9s�����@;vl8����LO>�d@����^��w��O?-���!C����T�>����>�|��7����5k�����zk����"�|�s:�6e��G�t��M�>��]7k�p��{J�t���zup���Ml�U����6l���d��;����$I	F�����,����"X�TVV�}�=z+}��z�����w��]����GJKK��b��������_���.����PBB�,M6����_���SMM������;TPP��uEE����u�y�)%%���Bmm���]�s�=W			!"�|�sZV�t�M�;�m4�!�����������UQ7���Tr�D�~<�t�H������������6�M��-����|��uJ�)���'�l�����A��])J#��w�}u-���:w����2�������-[��|�����2d���[��'J2g���[�3f��f���z���U__��8�|��_�����\���k�v��9OHH�4�i,�,<;��Mh|���������J��G����6,���D��#%:�O���%���N�����$���G�F���w��z+���������_]�a���^}��f����S���^�6m���a�����P������%IS�NUff����/I����_=����9s�~���h��z���t��7�������\���y��������?������a��5J������z����z�!�t�M�����j����*���+��Bw�uW��5y�d<xP��s������j��U
��JJJf�%);;[�W��-���3�8C����9s�n��v�~��[�����z�/1����������UYr��b���������g�yFw�}����K;vL�
�I'����f���vI{qq����������O���X����j���������k��n�P��0��j���;�
�L����m�����@, ��(''G99,Q���"i�L��o�eeI���7-1Q��1g��7��l��?��+,TX�Cl���@0�[�f�i��z���u���7k�ZTT��xEE��I�[2�]z��kjj����I�{����R~~ F�XUr�D���v��o�h�;�V�5k����?i��1�����y�"�������9�9�K��5ef��[�0���|Qr�D����bp�V��_����"]p��
m�i��������m
�M����G^�����G�+�@���|
�����N<��P�6�/Uo�KK���vl��m�	�������p,-9MI�I��Z��V���{5o�<=���j��}��@���T�0�Y���.�6nl\���{���6jh-w{����[�y�vO������3�l, �[���/�+�������w��N}-?�������T��=+K:x��x����'m��o��'�'i���>��Vj���CP�V�6m�6o������"q����2���\�n��0�[���v��D5��7{������d��L���;�C�� ���;�����Wk��Q�
^i^���K��v���w�*M�������B=�z+egg+%%%���+�*�������I��g���j�F5�=��D@o��{L��v��,Y���{�z8�e�������7~�|9����Lq���B5x/T=��� �[����Ree�������d�"q�7����T����R�n�����B5v�K�{���R;���
��c-Uj�\/U1B������*�S��
�2��Vj�P"����i�B=Z�m�vWK�/6��S���e�i�^���h(WQQ��\
�B��J������K+VP���e�-�^���������{wu���e�s��&�0d�ZC0B@,sU�=)��5--U���;Z/�����%�V���j�������SO��K�*;;[�f?�������$��0w����LV��T�j�h�]���ce�t����V����%OKNSR|���y*��4t/�=���k���a6��C�i����Q����Y�_��>��N9�>c��_HTjy��d_����c����zB���J�,U�/B����LxN�8��B@�RAA�$�0�}��JNNnx�j����?���C4:@,jK�v ��@�#�{i��-���/��B���
�%&&j���={v���A�*���<�O{���bz��ut/���������k����S�\n����t����[������^���$m��=�!��n@@o��K��zH����x7��{[������UuU*�,H@g;�G@ B=����S'�����k|�J�dO��`;�G@ }�������K�H�]fVu�R;��k�f�2v���@Y��i���}'-\(��H��I�'�K���:tD�@�D��k���v�vzc�����2!���3:ART$�����<+KZ���������	����t)��f�-�o�$�wH��I��!���5ph:APT$M�$�l��KK��+V�!����o��V�n�I����������ry���W���	�t�j5g�]l����5K���n��q�<3S��r}���Y�L:{�5���Ux�X����w^����&��kzk~^i�t�P���_�����a�++���@��������=���os��xt,�uA��{�W����Q���z���'9�:��kVk����W�a�;7���Fxkk�sz�@�!�`�4f����8�f��ybo�VXH��X�m������h����@�}����+��]�H?���ZV���cRA�Y�UX7)5Uj����za!-���� r������;v�~���N��r����
��[,��I��g����O?m�Rk�#��s�SZr����<���4����=���7������R^��u������6�%wu-"��boMk9zN�m��=,������������<(�[�y9z~>�����boI�I�>c{�B:B����.c���F�0g��|3i�,3�{
������Ypo��U�Ui}�z�booo{�����Vr��=-M*w��d�I{���������ce�_��k��k�:~U�U>]�tZ��2vO����2��	�����UuU*�,��q
�@d!��%���2vodd�o<����uuu�U�K[�m���;|��F�s�t�h���ju���-�0+����w��/���_������9[�cu��K�s�t\p��<5��{�{�R�=�yS��-�G�$.� ����7�-��{��������,����k���D[�������)m�H/s�:t�X���}�M��'&�J
�i^�m[�6��@!����m�RS��ge���e�h����������q_<�� z�1�y����������e�,cGS��DM.RF�~z����t�������B+5�t@�rU��sg��������cY0���t�!�@� �b��R{�bp�7~n���.-��:��9I�I���%4����9�Tj��Uj��qv�}���f�����$��������V�6l��Q�F)>�����Yr���A@�o*�:$�����U���}�o��>��J�O��shH�!���UYr��RBB����^r�7����m���)S;��.Ie����<_5���U�Ui�_�j����H�py�/�Kh	�Wco:�m�J|`��3u�	����3������������#%��d?�K�=���Rye�_:-!�"��j�YY������k���j�"�cG���Wj��
���%o�vR|�������+�}��
��Uc/-�.���5G�6~N����M���v�|��������
�vTj����y��H@@D,o��{B���qU�U�mF@D,o��{B�v.�B=|�m5vO���3������Z�n��������l����$�%���������Z�IR\�T_����k*�G�������;�����E�������[���6`�@s�{�������j��}0`��x�	
6���^}�UM�2E&L�[o���@�k�&����[�W^q]�]r�#�J����H��+��}��-�����~1�E�O��t���Z�|�


�d��u�Y*,,��q��}�vu���r��=�={�rY�	 xj�&�����*��%m�����	������w�j���3&���0Rr�D����������m������?��g��X$z-Z�H�_��O�.IZ�d��y�=����3g��k�V����J��7O��������8b��Z��l���f8��s}��"�m����=z�<D\��Kf���%��Z��m@T#��HMM�6o��;����X\\����M�6������S���u���j���-�Ouu���������$����|_���C=D����w�!o�����N�������V�����4UY=qO�����uJ��$K��{&Y��)�����yn�+��"��
�g�!��Hyy��V�z���p�G�����\^�a�=��s��u���3�|��7����5k�����1���kC=D(����}{wI�[<�?���V�<��y<7ms���*�*�����n���������p.���%�9{��-���������M��s_����zn*++��>�=B=zTW_}��y���y����;�PAAA������y������@�k���Z�v��=�\%$$�t,�,<;��j�6l0���esXr����U��~z�&�p��a���)��}����<7m��,x�%I_���S��-��H_���G��A���v�V����xv��`?7����IKK��b��������_���N����K{����_�p���^������o��N��k�N���s:���6�X��XYxv"��ViYY���fEuw������j3�;�K3�x�������s�2�{�[���V�e����#�w���q�������
|��_����?�ILL��!C�n�:M�8Q����[�3f8��)���/�p8v�]w����Z�x�����1l���Vi��������]Wk��6_w�J�vi��ME��xSY8#��PAA��M���C�j��a*,,������O�:U����?����t�i�9\��sgIr:��S�4�1w����Vj�����6�s�;zM�<Y�=���}��i���Z�jUC��������p-�J�<�soZ�!�%�')-��,D"z���1���vI*..�x��e��? �����	�}�^�Q���;OKNs*<@�!�.##���������\�8 f�~g�����{���:v��u��"���^�nfA�������n�\b�:��\�JKIq���Z�����U+5�Z;���]+�/|�����_�3�=�Tt�O�����Jo����ZE��1/O���}����v�'�Vj���Vj�g���o��mD@���Y��L���s5�]�<�J���&Lp����W[[���+������$���;���RT�z�����k�i�f��J-\���zN�m�����yO��Ft���Vs���,yKh�9Z�_<�S�6 ���~���v_�*���{���c�oC2:8����|[��Z���Z[f�i���(���V�J����vZ�F[����B@���]����R{��R\�T_��^�!��J���*
����\���,i�i�B�bp4~n���g��~�Vi��n�y ���
��#�@�r�.��o�G�|mj����y��Vi��}��i���1������^z�5sV�Y�F�*�K���@�3om+58t�A�h�v���&of�����}�v�!=��@x!�@jK�4;��;�f����J���N�vi@"�@LjK���y����V�-$��Bp�:��3���������N?s�]UtU@����[�1s�DF�������1�i���ti���p�.���-��
��#���|t�r�z�KR\�t�]��������� X����:���zi�i��!�b��ys���������^r��D�e����{��4k�4a!�[����j���^r����e�iiR��Do�M����t�x�J-��g@t#�@�s���S8o�=����L���mm���2v�"���y�7����:��n	��2vb"�������ef���\��)�+��|�O�KKNSR|����I�I���%����|]�n�u^XH���Q.�S�����2��1C�A�Wj������u�l��i�s8
T!��N9p�"��J�		���/c��S���u�s�:Dw��kk?7���.cOL��VjM�����iW�.m��E���*;f����X%��}�vT[�"�@�k�R�aH��R����������^���sw��K|E@�0�R�v�M:tHz�=s�:��M��Kn�k+5w�d��5
��^��R����)�w��wK4�P!�@�*���%-^l~����L�{w������9��]/;V��������`T�E@� qW���T��R�����<�Wj����8�M �����@ � <zsW������Q���Vj������K�	��@0� Z*����
i�<��g��L��D@� ����''�$�����"���22�0����{��p���>slt�����8CJL�j|(6�E����{1�E���$m��A�F�R||�����2:���	��@���G����j��)�Fc�����������r��%d!��i�uz��*K.���AJHH�{���>p��|�i�B����p~�URq��Bo��{FC�P�Jc�:�th%w����Vz�Q��~���k��q��Bo&Dv8O���-�*�\�r��}�zmmm�����
���{c�^3��+��E��1KN_r���
m�g.���Z8*�,�v���hF@��+-m�=��r
���n�*�������vi��� Z��w�����hm�F5v��!�@3��s�g��^y%:��z���"p��@�#�@3��KK�l��i�??����BR|�rsr	� f��o��?�����:�Gr�4)���i��@�k^��[7������^v�L���Uc���{2K�@LsU��}{��DC5�@{c����bB�Y��\���]Wj�������{����O����v�hD@�\��gfJUU�+�w�j��Gc5v�:�����yii��:$���9SN5v����7tQ��~��8 M���1��"@�����'�K(������ jy����H���i�<����_tQ�y����}�O$Uj�,�;�3�?:����R{r��kCJM��J���%O�$�hr�2:8��g�9���D�U��C~��N04f��~�������g�����	�=��i�<���{t�q�<^�P-Z��~�yyx�W�t	;���C\���z�)���[III:�����'��=��g�Qnn��t��.]�h��������y�����f�rO��-��_z�e������� ������UPP��s����?��4n�88p�������2e���}m��I���:���TZZ�����~�yyfO����Z�H,q�E���������%IK�,�;�����^s��q:���^r���g��o��u��i���A3
����]8�,(Tc��������u�w4������c�i�&��QYY���Z����=���Z���
_WTTH�jkkU[[�������������|���0l����>�N�~�J��������������L5������/��NI;��x��]����?���������/����|�z�����j��G��{��������{�~��������������k��yN���Y���zP���kC=��U���������K�N=��v��&i�W�$_�l��W~����3`/�9����Z[��!L0T��Fe%����L_����)��;�����xv��`=7�M[� ,�#��?�W_}U���JJJr{�w�������+**�����c�n���j���:��s����� <������JKCvz�MII������0lJM�����qvYY�c�Y����4(�c�s7K~���j��p�i��vi������W<;�E����Z�z�����b�h����������t��.\�P?���{�=�q��m�����k�t<!!!l���� t���_�����}�$CIIRU���������!������/^v�L����T����$���G���<7��|���g3��C$11QC���u�4q�DIR}}���[�3f��n��z���z�j
:4H���*�]�H���t�-��g^r�D��������������w�ZZr� ��C���@��M���C5l�0����
U��N����L��?_���#���{���/����{k��}��:�C�!�>���Zg��������Iii��=������w�j���3&>�-��+�C�%�Z
���@��Ch���:x��������O��U�
����(.��U���G���h��I��;w�����`�YQ�9[�4�{hD�����_>z�M���j��������b=�f���vI{qq���{��	���**�&Mr^�����]�^��\���E@������>sw��k����q��e��$�')-9-��@����e�iiR����?uU+,TP���B�%QE�����y�E���_�[��m8OMu\���J{4pW����|��e�v��f��7�2������$��������'�\�.5�3��^ T�v���t���e���b��?*��T�D@�*Vk���w�&<��u(����R{����1�O#@�#�h�������}��������g�e��=���x��\i�o�i��=1��kh,c@$!�p�U!��T��e;�:t.�+��G�+���&:'��3�s���i������]��o�VT��|�Xk��f@�8�3�Z�+���e�n�����B������3�q)33�������Tj@0����>s����Tc4*� ����>sW���rs�7w����{�R;����3o�i!8-g�G�r_���������3OMu\���Bp���:������D���3�5s�����B9K.�����D����G�h�>��<�,eU%v�e��t J�Z���&�����Q[��{Z�H��DM.RF���X��H@@���e���s�3�2v�>���:m��A�F�R|���!���D���K�k�>�P{��#���UYr��RBBB��&:�����]���>�P{��0�����v_���������@qU�����[7�����[�g�bo�+@�"��]!���=_g_��s��q�w��C��=)>I�9�q�$:f�/a��5�{S�0�i��=1��k�T���1K8#�a����,����+����2�P{�Wc����	wK���V�;��{<������2v�������2��p�|{���h:���[���2�����7�j�{Z����}����_��\�����g�,9�?t ���3��{���7Oz��"rw.(Q��������@Wc���?t H��������;�?M���:�D�������g9���!���}����7-�yY`����:m�g��������|�����2������Y[�����9S���y�1�������?����9Wc/��2v���G@�����m)%�y~��y���@.c������<�_������;����gK���:����w*�������8��\JK��go����&�}�@x#�-h��}���w*�f�����[�/������
����G@��ss�Bm������3�n�4K�$m���,9���y�BxV�4e��p��Ly����Y�P�;���i��������>�0�<d��bo@d#�#f��g��e��n	��2v �������gy(�����^t�����JK[� ���\a	;���	�����Yr*����������j�!*�F�r v��Z,f���%bQ��>s��C!�-cg��+tD
W��SS�bo��h
:���}��[i� 2��z@[y�g�bo�D���#�4�c��k~���)�bo"����,i�D���|t�W����^�
~�!�h<�[����I�R�
@d#�#���%�qr��_�O�u���K{t��?��z�|�mR|h�9��aa��%���B7K�2v�F@GP�)Qye��V������GG��m����#���{j�{�������j��}0`��x�	
6����������n���G'�t�y�]p�A��J������TU�$�w�^�gp��Yr�,.��e��/WAA������?�\��q�t����o��QS�L���^�-[�h����8q����� ��7�����<������!��Z�h����zM�>]��z��,Y���d=���.�_�x��?�|�z���������~
<XO>�d�G��5t�M�7��%�!RSS���7��;�h8��c�j��M.���i�


��7No��������Vuuu������Z�����;h��>����|��uJ�)._����2�3��=#0���������/xn�+��"��
�g�!��Hyy��V�z���p�G�����\^�o�>����������?_���s:�f�%''�0r�}��Q�C`��`$�fg��J�\�^�2}���
��]�6�C@���/xn�+��"X�MeeeP��#�G�;���a����B���:��������|g����p�f�����^�R[[��k���s�UBBB����s_���W<;�E����Z�z�����b�h����������t�������|Ij�����k�t<!!!��X/�!�'�')��<�8��9F����/xn�+��"X�
�f�!��Hbb���u��i�������z�[�N3f�py�����n�:��5�����k5|�� ���,����XD@���M�6MC���a�TXX����k������S�*33S����$��9S�G��c�=�/�P����>��3=�����6�������$���������Ks�!4y�d<xP��s������j��U
��JJJ��	o��z���u�]w�w���N:�$���[:���B�-�JN�m��]����Z���}��F�<YC���ba�@�"����3�.i/..v:v�e����.��'�SNC��V=�2]p� ���yq-���@ ���:a��@ ���:a��@ ���:a >�@p�l6IREEE�G"������RJHH�pAxv���������/����3�=# ��1����������@88z��:u��a@�a��%1���^�}��:v�(�0B:���
eggk���JII	�XYxv���������/����l6=zT={�T\���3�1&..NYYY��������Oxv���������/���0s^�5	a��@ �#d��k��s��]�v�
"�|�s_���W<;��
(@`�0@@ �t���SO�w��JJJ�Yg��O>��������N9�%%%���O���+�4R���<7�<��rss��Ku��Ec��m�9C�j��9v��������;@���>7��M7�����k�N'�|2�^���>7���������o���l�r�-���
�h>��C]|��������[o���5���<x���k�������e�>N��|�rh�������5`��7Npy���5e�]{����e�&N���'��/���J�}n���5e�������i����u�y����4�#G�������g�f������ �����MMM��=�\���G+V�������3�(333�#G(���y���5g���;W��m�s�=�����w��]�G�P:~�����z���w���/�Pc�����[5k�,]w�uZ�zu�G���2l�0�M7�����j�����6�|��_~���/����Yg�e����q"����i������cG�����@
a��g����6b����>k�6m�m��	A)�Ik��?����O<�VSS�!"�������l?�����F��q"|I����������l�����pl����q��pd5f�555��y����p,..Nc����M�\^�i�&��%i��qn�G����i���R���JMM
�0�|}v���>u��]�^{m0��0��s����U���M7��=z���N�C=$���a#�|ynF����77,����o�r�J]p�A3"?���P����\V�U=z�p8��G}��W.���o�������q"����4w����g��N��!����l��A�=���n��"���|��7�����+��R+W����;u��7���Vs��
��b�<7W\q����5j�(�l6�����n`�;<r��qEE�~��G�o�>D#C 1� j<���z��W���o*)))��A;z�����j=��3JKK�pA�����{w=���2d�&O��;��SK�,	��������C���>��s��w�������3��# ���d�X��~�����Wzz��k���[u>��/�������������g��a"�����k������/���X}}�$)>>^��oW��};h��/�ddd(!!A���X����o�>���(111�cF�����}������u�u�I�N?�t?~\����t��w*.�938s��qJJ
��Q��
���2d���[�p���^�������]^3|�p��%i���n�G������������U�4t��`a����)���/��B[�nm�s�%�4T������"���3r�H�����:����_+##�p#|yn*++�B���<6�-p�ED�g��*u�^�����]�v�e�������~��_�:w�l��o��f�����j��9s�����l�������m�f�;w�-!!���_��[@���y���m����+V�����=z4T�B���NsTq�M�}nJJJl;v���1��}�v����7[���m<�@���@k���s��:v�h{��Wl�|��m��5��}��.���P}��G���l�b��e�M�m��E�-[�������l6�m��9�������o������l���[m��m�=��S6��b[�jU����O�rrrl����a������4�6z�h��i�����l'�|�-11�������w�y'�#F8h�s��W/�$�?s��
��r��;�)z�j�s�q�F�Yg�ek�����O�=������� ���������v�������kKJJ�egg�n��F�?���#d��}�?����i���F��t���m����O<��t�����e�l�� ���@ ���:a��@ ���: //O�f�
�0T\\,�0t���P���~��/�X={��az���Z}�������OV�v�����|����_��K��K�U������SO�|��3g��g������W_�����
��Q���C=�����k���n_�����w��W^yE��i���GyDyyy��m���������K���O���O�`>b�S]]���g+33S'�p��:�,7��l�2u��Y�W�V�����C���*++k8���N7�|�:w���]����o��i�4q�DI�/�K}��Z�x���a��gO���7o���C����#Fh���A��v3f���M������_���.��2�����c�$������O<Q�����O���[�]w���������0-�@&I���Z�p�������?TII�f�����#�<��^zIK�.�G}���
����/����u������Leee���nx��;��c�=��>�L������k���L%%%Z�t�^�u����o���={�F���K�J������?������^xA��-����5i�������b�����D={��$��=[�V����K��CI�jkk�d����W��������<�����;����\����Oj���
�w��I���JNNVzz��8|�A�=Z�4g�]x�����RRRR`�q��/��BV�U'�|�����ju��U�T__���j���
�=��s2d��o�������@��2IJNNn������H��9����;	�X,2d������g��poI:p��rrrZ�MZ���c�X,��y�,��k:t�d����oF���%���%��:���$)!!��5�0d���6���7C�����4h��V�8���\���9Ruuu��kW�/m���kIR�^��6Vx��@����t��I=z����~�s�9G�d�Z����k���
�%&&�j��c�;vL;w�l�z�����u�RSSu��'��+����S��c�i��A:x����[�3�8C^x��������k�Qaa����u�M7��s�uZ���@�8"H������{�n}��'�?��y�������F�����o�����k������f�%�w�������g����3CA��g�i��A4h�$���@�
�=��#IZ�t��N����������&N��O?��a�Q\\�����JKK�9���/�P���������	�1�@�Y�t�x�����Uii����t��g���.����~������S��b��W�������l~����6m�N=�T������{w ��yyy�'%$$h��y�7o��sz���7�x#�C6nH���^������_����?�� &1�@����5k�h��������O>���w��+����Y�A ���i��e:��35r�H}��z�������c�;a�t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0@@ �t��0��aIP�;�eIEND�B`�
int_sort_bench4.pngimage/png; name=int_sort_bench4.pngDownload
�PNG


IHDR���(-9tEXtSoftwareMatplotlib version3.9.1, https://matplotlib.org/��!	pHYsaa�?�i��IDATx���yxSe���_�S�@)�B[�b�Y* �����n8�Yq�
pC�2�����.�L�E�0��2,�(� X�R
TJ����Lc�&'i�4��uq���sr�n���g��axU����
t|:>�@���@�P��(����
t|:>�@���@�PRR�n��6o������b�h��}�����P��>�I�����q��O�����{;������k���N�b��h���/������k��m�p:��6n��3fT��`��]Z�pa�����{��1�m����_4c�
t�	!�������B@8s�������������By;���A|������H���i#��Rj�v��o�s�7l���'�Y�fj������?����:~��F��&M��I�&z��eF��,**RZZ�.��EDD�y�������_�������)%%E���W���u��Wj���^g��~�i���+22R�w�}�\�$-Y�D��wW��
��;j��9������t���*::Z������������:'33S�EK�,�c�=�V�Z)22R��_u���J���^dff:#��[|�m��A�:p��F��
�Y�f�2e�l6��������M���q��	�X,��_��8v��!Y,���+U������}��q��j�����o�Gy��9���w�����+""B�;w�[o�U��}���b���_TZZ���m���p�������%I��~����x��js�"z�4j�(���z���4{�l���H��5kV�u&LP�-4c�}��z�����qcm��Q���z��g�|�r��/Q�4z�h������x�b�~���8q�����y��i�������Z���^�Z��
�9���������~���s��Om��EIII�^��O��������5|�pm��EC����g��SFF�n��F
8P�?��$i������5i�$I��{�V~~�&N���M������W\��K�����*u���zJaaa�2e�


4d�M�8Q��_��#�(99Y�_u�����d��4t�P���S/���V�^�Y�f�m������������h���������CIRVV���������':�I�%�\Ri\�}�����?�S�Nz��'���w���?w���o������{����6m���?�m������;��b�-��3gt��w+<<\W]u�N�<�'�xBw�}�RRR$I�{��2g��2�����/�$c�����k���1f���E����C�EEE���z�2,�q�=�8����F�~�����I�;��S�uV�XQ����t�b���G�u���c�G�.g�{:|��f�1�T��<��!��{���I����(�����s&O�lH2����N�<i�i��HJJ2l6�a��v�ZC�q�9������������k�V�n��6x��1�$��'�,u�k��F���]~������_~�0�8~��d\{��F����M�8����.����={�!�8r�H���������~�q����F�^��
yyy�a���{
IFTT�q���R�����
I��E�*}�_0��C���NY,���={�0�y���c�������~��'��?�P�5�����������{w5h�@k����5srr�m�6�v�m���v��������Wz����u��Y���b�'Ov��6n�X�O�VFFF��,_�\]t�����8��A�}����o�����R��3F���s��(��6��{�������S���Y3�w�yZ�~�$���?Wpp�x�:tH?���${z��}K���7n,I����+�+�|�r�h�B7�x��Xhh�&N��S�Ni��u���������3
�2��_��/�\-[���b�G}��=V�\��/�X
6T�f�t��W��3jEbbb���5�$%$$�;^r���?��'N(66V��5+����S:|�p�����?K���o_����d�������U^��]�R��5k�&M�T������s�=W��
S||�����X���kT[���i����P{�m��O����Z�O���k&`,o���"""��M�41�:)))�!�YYY����z�����heee)//O�����+s����O�>������ys�p�
���J�?�����k����e	�4s��8}��:w��;��C�F�r���{���+�Tjj��y��8qB��w�F��-[��@�������>n�X����H���z��w*��WS�m��i���������g�i��E=zt��e�E�9�[�m��L�R�gq����E�O�f\�k�������W.�O?����,����b��o�����R��-UTTTm�^�^=�_�^k�����~�+V�������^�U�V9wE��2
�2�
�a��U�|AA�}�Q���{:~��:t����^����$m��Y6�MO?���7�S�L��W^)��Z�"@����yZ��m�z�j�������u��������s�N���T�SU|��?��s�9�q���#N���/�\�_~����4n�8����z�����?�A�[��4��1T�6�Js�]n���4h�8�?������{-X���C���6��^��������_��~X�}A�W^yE-[�T�����{�j_7((H�����K/��g����>��k�j��Aj��������E��F�b������M�6i��%���ot������.s����������h�"�l6�8qB����A�Q��%�E����k�����:�l6=��S��+,,�2���8u��Eo��V���o��U�Vi����^[��b����z�����������S�N���%i��������i�&�y�O��k�����$�����Nm~/���v���_]��{n�=l������i�F�Z�����e�Z��OI��}��=Z�t�.��b��T��w���r��t�"�t;}��A�������;w�4h�~��U��h�Q�������l-Z�H���j���${���+�h�"=���j���V�Z����N���d����W�*�*R�[�G}T7�p�BCCu�������~���O��f���m��i��!


��?��?�Ps���5�\S����_4l�0���Kw�y�c��F�i����^W�O���3��?�Q�����[��g�9����]w��c����K/U||�~��g��;W]�tq�]{�����{�i��a�8q������[oi�������^n�[E�t����`=���:q�����u���*66��k�g�����9�w�y��T�����z���-Y�D;vt���[7��__?���n���j_��'�����5b��n�Z���/����x���w�}�^}�U�v�m��y�����t�R}���JKKS��
�}��m��q��Z�`�6l�����g���W�_�@w���~+���s�=�����5m�T�t��A�;Vc����7������k�QFFCp��/�PO=��,X�+V���H{������,X������W_�#�<���%%%��[nq���2�
��+4m�4=��


U�~�����W�8>���������v�Z���S�V���#����[n�k����_~Y��W�-t���k������y����q�z�!��;Wg��Q�N���������-Zh���9s����N�l6�]���2g����-[��'Oj��1�"��?��5�:�z�]PBBB��W/�^���Q(W\q�����7�|S������Q�~�4c��"z���Sff�~�a���[���S����h�"�v�mN����P���[�:u����j��E��K���R�b�X�l�2�9R��������o�w�}WnQ�
�E�z����b�
}���������*!!A�6m��_\�o�:�L�\����e���V���tt��U6�M�����������h�l�G�:g��b{�����k�������u�e�:uJ�w�v<��w��m����h�{������5z�h��5K]�v��#G�f�u��I#F���#4{�l=����!��<��Z�n��]�z����m����������rEx��!�edffj�����3F�/��j��O?����o:p��bbbt��k������$i��%z����?(22R�z����?���;���~��rQQ�Z�n���G��g������(������@��H��s�~��5l��}�~�0�<yR-[�,�����]�+w�e
tI���������_�����#h���l�L�.�a����I���r�~V�U�V���!C���	�s�3�������n��������h���e�A��C��#w�!�y�]�@������<�A 22RQQQ��p�s�3����������������������G��C���v�\7&���(��������f��j�V{��jUHH���9#��V����.44T�������.�<O��6�G���0t��A?~���[�h������E{j�+�k���Z�hA� ��.�O��6�C������������q)**��S���AS��2grg����u��aIR\\\m��2����n�h��5����l�M�6u����"�={V|p����W��$�������e��������f��h��Q<�-22��������3�u�����P�;�9S���	.�����C���@`����X,U���x�b5n���b�@�"q��f�����).NJI�X'�b�^z��t����4o�SQ;���}"[����>��F���[�x�&O����w�mU����:r�H-G�;
�Z��.M�$����������F��^\������jDe���Y��{/�@�}"[������3���]�wQ�;�E��g��PK���1mk12;�����t��kJ(������k�u

4q�D���*""B}����_]�5�/Vbb�"##u�UW����U���Y�?^qqq���P���5s�L��������+��AEEE������C��O�>]]�t�����6m�(22R�����u�4g�Y,Y,�����\�7U��pC�6mbo�����[��2I:Sx��v������'��Ttt�Z�h�����:����������Y3EEE��K/���I��'������[�EGG���/v\���o+!!���.]��;�^�zj���
���O;����O*>>^�������V�X��v��}�X,z�����_?EDD��w������'N8����	|�3������oU�A5�tC������"��i��u��&N���>��Ge� ���GF��q��>�����z�����uk���:t�v������r�������;5s�L�9R+V���i��|�������?��>�@���������/�������Saa����^]�����t�c����������tY,5i�D���S����OJ��5k���c������x���>]

�����0�o��a.**�i�i�
�o�����o��t���*���tyu���zK������/�i�&�v�m����,I���kU�^=}��gj���^}�U
8P?��������Keff�G����oe�X�u�V�:u��������������7��^xAW]u�N�<���,����3g�f���W_}U]�v��o���#Gj��M�����>?��f����]�*((Hiiiz��'�k�.IR�
\�	���C�w���r4U�@7!?_���	����{��G�Q#�^��)�~���;}��^y�-^�X��
�$-\�Pz��7����f��9�������J��=�\m����o�����V�v���o_Y,�n�����5k����j��������o�\����Z^x�${/����75k�LEEE���SXX�"##��E�>*+�|�yI�aQnn�6l(��A�W]�o�W���-
�.�[�9���R�0'�:u����w�v�4o�<�Y�F���
��W_����
����_|Q}���.]����[���Wff��L����L
<X;w���
t�e�)33����������B�5��^w������/���zH7�p�$�������k��+����^s�7y�d�*1?�Q�F�X,��|�3C�}	C���={��j��O�>�c��������cG��5��c�z��Y�X�^��|��n�M��mS���5q�D�Z����J
�;�����q�R1�n��ruVN�g�����S��qqq:|��$�?���N�:��M��A��?{����={$I�����
d���n�:����Q����/��{����_�kw��YT��u���j������_%Iyyy���_J}n����{��~(u�G��H�
g���z�M����dW��8**J6i�����|�t�%���/���������>�L�W��u�]�A�i���N���3C�O�99����P^dh�NM��a.**R��<E5��7��q�w|����E�j_�U�e�1X,I�N�:����R���oyz�%�������e���_�g�}V-Z��s�=���;�e��j��]�����m��Q�V����s������/�T��M�~��Ps(�M�X�f^Td�sX��4d�}��*�h���2��[��m�Vaaa�������V����kM�<��k������_�:��_T�ZQQQ����u�����k��e�]�c��)99�1'����������u���Wy���P�l6'�)������CM����}��n��b�r�yQQ�l�6���z����g��z._wW�n�t��A���())��s7n�N�:i��y


�y�����X]�����O*�^�b��O�>�����x�	�n�Z��-Sjj�Z�l��?���=6n����;Wy���0�l�
�l�J��k��x�g�����y~?��������x�EGG+11Q/������u��wVx�����O�������+�r��*��K�K/����8�B1~��Z�h���k��A����n��f������P���S�~������/��R���S�
�� fd�?%������;�������O@@4h�z����#G��^�����_~�E�~�����*G�����;W����*::Z���z���5��J����_j��52d�bcc���_���#JNN�$=���6m���m�.]�h��E��m�^y��*�NJJ��S��f�u��Y������!�?-
WO-5JZ�Tj�����x����������W_�[o�U��u�����r�J5i����/��b-\�Ps��Q����j�*=��cU�F��
��/�G�����o�>-_�\AAA�X,������I]r�%4h��9�������~���+88X����5k���lS9_0j����R��3��KK�����kKLd�"B"�<'"$B1�1���,��/_�K.�D��~��=�\�p�
������ys�y�����f+5�����������k���:��s��c�i��Y��d'N����T�������V�X��>�Hm�V�p���u�=�����W�f���/��pG��p�_����u�-���|�PK���s~��'Y��w�%//O�5��'U��3g�h���j���""�n�����^���f�����c�k�����sWY�*b�{S�Y�V-_�\��/7�U#w�!���C:�|�Vj��.%&����"�rWU��j�].��R���%6Jt�=����U	�6���#w�	��m�����uw���G����d��/����c��.3��KU�bP��]k���"�������Z��(��OiO�IV��n�Z��	���6
tjAq�>`�w��.��>����T
t<���LEER��Y�x��}����K�5��HO�&M���������Wpo�N���"#�/�^���xH���@�M���m��.����JW_-o+���V��P���6j��@wR����'j[EC�%{�yU{��[g�����b����$����O���s_F�^���0��_~Q�f�&��R�5EEE:{����9���$�������g����#


R�QjAeC���-}�*'N�{��.�H7�v�v��;�l�fv�yI��(\Y��

R�6m����_~���k��o���z��U��������H%&&�a@��l����i��o�d��+���5����S����m��(\Y�NSbb�
e���=�j�j������KLmn���]pp�BBB�����l�a�jX{eC���>T���H��������|uQ��(��d�X�T����BEDD�A�E�����r~�+rr<�@B�\;�_���p�<��,
t�PS�t\\�������p�2��,
t���yJk�T��b_\�xxPZ�s3�������(�(��Vj?�(��Z��DGK;w����CRA���f/�K�S/�����qT��o�V
t������q�����"u�(�|�����m����b
����m��C��*�J��!�z�T�������{����E{ ��'%%���.w|��q�?�"�u�:��,
t@��j+53J�s�[�}�����?��}��k����Q�2�s^V����<��Z�}�U�f���E��O>�Dm��U�~��>�t@������������z������*K�*ye�����8//O�d�Ze�Z������W�!w�!��;��s�
M_�m��1P�^M�I�>�v}���?��n���sf���3f�;�j�*EFFz,�����+��;��?���{�1{���|M�%T�|��r�<��q�����w�u)�/%����'����yio����
��-[Vz���S����x������
2DQQQn�`�Z������+44���r��g�s�?�/�D���v����'�����+��y1�I���k��E���]�(0�(�/8X�3G�����MH�n�Az�E�c�9���?����W+==������^�xhh�G?Xz�~�������G������>���v0��ZDH�����FWj7�;w�M����#������K��������+�U����yu-Z���X�1���|��������~}�����6j��@@��-���~}i�2)7����y�sgi��E3f�BB��p�Y�Y�5��[�U�I�~j�:d�4x�s��>��X�z����u�wx;��>����\I���^��wQ���tFc{��!Cd���X�'��~^{�s�	: ��l�����-}���������n|�5f��*
t@�V� ��Gi������)���}Ewz��Pg���_i�����$8 ]s��t)E:�]A�����n/��*�+R<mz����;@M�@�96�����e�!��o�/"$B1�1���1�P�de��s^����@ 2������VrLr���D�(�Q����U��:�S�u\�g�@ 1��ZrL���u����:��q���X��x)%�3�H�l�(C��C��Sl6���h��1���X�_���[���Ja/����@�^�+���_����/��y�rrr�l�2�9��k233�������N			z���t�m��J�����je��=>�^���3;��,����s������sg�q������{�j�����{��;�h��5������C��B�_P��Zu+���Kc�J�������-m�h��g�N�9�33����}��a6l���/X�@m����Y�$I�����a�f��M��f�23�EwU�yt���R������k0@'��>��6m��A�J:t�6m�����%=]JJ�
�~���c����q��|��UT���Kk��������o��^�z��)((PAA��q^^�$�j��j��S�=<q�@C��C��#w��F��-������knq����e�V3����;~f�;��@7c����1cF���V�Rdd��^'##�c�
4��=��<r������&�7D�,g�sI���/�|����
fs�����H(�������^�U����-Z���C�J;t�����*�=���S�*55��8//O			2d�������j�*##C�Vhh���$��=��<r������u=�|f�j�J�2���
qw7w����)�Z��m����z�^��|��R�222��W�J�	Wxxx������`���r��g�sOm�������kn��9RD��~o����W@M����l�V=�[$���S��m��m�&�����m����-���=z�h����s�~��'=�����s�^~�e}�������>���9n|��t)����s=�����5`������c����������(�%�M�6���Ou�}�i��9��������kPG�l�?M�H��Z�yUm���|�@�����*6�]�xq��l�������ti�$���������[�P8�v����\��"�
�OfQ���s:IO���F��w���C����@e<��[Y%�+�E��C��)6���%���������O�Tuq��v���E��b8�Q�|FEC�����c��.I���s�s��(�>��!�H��9w������s��@x��V���������k�=%��3��3(�^��U���X,������@�c���y;��^��ZZ����@x�;C�����K�V
�?
t��������pg��-��Kq�+b"c��=X�fP��.8X�3���$�ys�����F��q�5�h,Iz4�Q��{{���|�fm�{�v����p5�P�l6)3Sz�=�W��~|�(�_?�����������g��aXC]q�N]S�(\��n�5�Pk����$i�����_�����������7k�]�},)!�U�0��?�$
:g�B����W���(�������WZ/����k�g��rs�F��	���5���)9��U�p�g���e�����$
t@�IO�&M�z�s���3g�"�����/]Z���x{q��pT���#�zp�BB�%_��?s\�t���6�w�$)>*^9�r�[A��cQ��A�����pg~3)/�����?>j�t���������z��J��l��1N�����;������p}r�'�kP�/1�1�;������=����.�{�K
����HX�Y�'����+I�~p��F��yI��5�S��n5�D��(�M�;��a�U�����C�pN��l���^g
�x;��U��l�EII�}���G��q���:-7?�������6����M
k/�x��������o��8��zH�}�������?�A�-R�=����C��������AM�@��f���/�����vq^�0������1�������������>�L��5��?��&M�x;4�Ka��~�F�f�����-j���?���-Z��q�M�6^��C�:�W�Q+)!A��.i������x�����:t����Z�[�N�Z���q�4v��
�/((PA��{���o�=��*���U�+R|O�+��;��?��]i�����:��sw���:�ef�Q�=[�0������Ut�E����y���O?��W^Qjj�y�}����8q����4f��r���9S3f�(w|��U����X\�W�!w�!��;�=�{j�5B-����o���5���^~~�[�K�pYV�k���&�����9�x��t�n������@��***R�=����J��v�����k���S�NUjj��q^^�4d�EEE���jUFF����P��H��{��y�����J?����PR�$IR��F�k��P����*�Q��/�����+f:�e��
���5JZ��x�����������������_�Xrr�����Wx~xx�����

��sO�/��;��?���]H��e[DH�n�vE��������J�p�+s��*�G��o���e/�������9�o}����]�J�����uk/E�#HA���O�c�����Q��D�P��
t��RR��wes�%):Z���6iU���l�V��w�}�����}�Y]w�u������k����^�vh�7J�{��>��z�idSE�D�k���@�3�����_}u�����/\(
X�q�7\x��Z�l��N��'�|Rm��QZZ�n��fo�~��}�7����o�T�%TP���5!j
:��Q��#�O?-}�9���?��������0�/yb�s�a������(��	:�����_�x��S��j��.0 �9�&P��d�U����}��]��O�P��??�~�:S��D��Tzz�����#9b������w��K(�JO�����*���_x���e��~lu:����s^�j������u�����.�@�Wv�y����/��^����f��(<�f��.3��9�
t`�1���:p@���`=�`���|��u��{^�PK���k����vP�@��l��+��d��b��x���O�BC="��}�����c�%I�
�x�Zaa����%6J�h��y����cn�aX���

5h�g�	JK�IV��n��g�Z��S������ee9?��9|0�t@5UH����}�w%�+�E�P���i��P����o_&�PVM,���!��RR��x���X��M�_-���I��wnWp��b��pv8�Pp�4g�s����&-]*�jU���xi��z�b:�;��f�/���?_v��x)-M5����+���������HEE��/���X!����v�a�@������\�="B�:Uj��^p��-m�X�/9t=8X����}��j%|J�}�c"c��<P�@W�c�����������O�_/.����5%Wm7�J�������@�:����L�l��Bo��U�a��@��.]s�s��aH���{��{X�fQ�@d��{�
���rX��k�uPV�s=�e��y>�'���P��l��5�]c���SKI�����jb�9��&
t�C\Y���b����q�av�y�m��b[��D�uD��p��;�����F�HX�Il���@�u��E���UKI��W��j�O��>�|%%%)""B={��W_}U��iiij����������w�}:s�,��&����������f�����8��'��%g���lQVv���9s�Q��A��������g��JKK���C�k�.����;��w���?�7�|S�{��?���n�M�E/�����W��?��4}:E9��T�y�9��1GE|�@���4v�X�~������O?��o���~���7nT�>}t�M7I����t��7��/�������l�m�>��>w�R��*������sT��
��g�j����:u��XPP�
�M�6UxM������o�����E]��~�I��/����Z�������8//O�d�Ze�Z�~�����
�s�3��sg�I6X��#��m�o���K��X�j%]|q�j�-�r�|���#��>U�����f��y����7o�\;w�����n�I��������Paa����=��#�����35c��r�W�Z���H��D	�W�!w�!��Z�6m����w����Juq�v2�������9��_��?1����|G�	�9�3|�@7#33S�>��^~�e���S�w���I���SO�������S�*55��8//O			2d�������j�*##C�Vhh���$��=��<_���e��Bp+���{/��e�UWu���S���������x��0����=&&F���:t�P���R�-*������������K���cG�>}Zw�}�}�Q�_�><<\��������z�����H��{��y��;�M��~��4/k�li����k��{_��?2�;�
�Qr_�������
���0u��]k�����#%IEEEZ�f���_�5����������d����$+����+b��{�'L`Q8�S��3�f�T�.I���3f�z����.�Hiii:}��cU���G�U�V�9s�$���/�K/���]�:��?��������:��7��[�7>-��gxj�v���]>W�_��:r���x�	<xP]�t��+�egg��1���d�X��c����j���.��r=��3�z���8�������Q�<uN�!�f�������r���
tI?~|�C�333K=		��i�4m��Z�jGJ���>p��y��'KW^i���s*gvH{��]���6����uW��J=G�9<�'tt����9�5�TnB=��������|'I���{uA���@_5j�t�����V�x|�4v����}(<=��k�U����ov��CAE�>l�n�����/� ��>}�f��Q�X����s�N/E��;������v�
t�2����ZNN���Ai�&�9<`�9��\���W;����@���o���}�7E�Dh��]��(ZK���ti���{��j%�}���m_ �G�s�����h���a@�9SxF�����(
t���t�"peWi?p@*�1�?��e8���Q-[�TDD�z����3g*1���*((p<����$Y�VY�V�c)��'�h��{��y5����B���������n���9:x��f�9wf��'�������={�������}{���h��JII������a�r���9���uIZ�j�"##=WFF���h��{��y5��=�{<r�
6('2�#��	���g6w���n�.:xAVV�a�U1�b�}�s���6l����:uR��=��uk}�����;��?u�T���:���)!!AC�QTT���X�Veddh���


u�~�������W���>��G�<�[A�'W�o���������~��s7w�����@�Zf�Ik��v�aH������k$,��4n�X��{�voKPFxx�����

��KO�/��;��?�<�������u<~,�1=����������� Vq
�$I;rw���[��gHH�Oo��3�l���7:����sE����*u��)���G��z��C��Od����:Sx��s���;�q���'y;����-�%�6l���2e���[�}��i��������o����P����%��x=�P\Y�"�}��������������u��Q5k�L}���_|�f��y;4pJLd�"B"�,�#B"S�Q!P�@
����s���[,��ii,��d�o�nIl��]�wU���pxx:� W��_s���������l�@�Il�H�ZG�5�x��+����WZ���Z{N�}�yJ
=���j��s�K�1f+5\Uv��v����hs(��de9?��9����m��6kP\��<>^Z��9����6j�aUv�z��M6[�9���W>{�4a=�T��C�g
�����+���
6���P��*Z��U+�����h�����+�sNq@���=���*$$D9�r4��Q:k;��{��q�"B"�k�.�r�
t0��U��f���:��P����c���[k�5��Qn~.:|
:���*�%��9������vY��+�_E�&��J{��h���[��s��X���@�jT��+��;v�^�S�P�'Va�
t�BE����Kc��������}�������~��4m������9������v�9�������?~\��s�" ����j8�p�0/�V-%�3���o�>�l�r�

t��/D@��@�
T��aHG�J#FH�~Z���V
�������+W�T�F��m6���Y���$/D5+"$B1�1�(�*��\������^�O������Yg[5���#G:�>f��R����*))I�f�����3����c�+|.&2�=��s(�<�MZ�����[�~}�.�D:t��k���BB���������G���N�9|UQQ�$�M�6������M�z9"�\q��#w�nI�����c��-�[-Dx:����*�!�z�����tSq+*�_�{yp�}�s�_X�V�s�9:v�:����QDHD�[�E�D(%1�^o�i�V�*�e�s�8���~��g�R���?�����o��vXb�D��K���������'�yR�^r�BB�%C��f
@@�j�v�&Ov��|�-���7�x��a`���s��L���Jn���-��[\7u��V�8/�q�
�����  U�J��C���~_�����7�������{w��_���/����"HV�]-I��������+�q�����z���(�$gWi���5m���������?�P�9K�^�P�
�
�v�ZI����:��p��'6J�G�C�  ����}���v�Zo� �m���'�8�������Z����ZG�  ����'?p��y���RQQ��[,����x6N��������^�@]�}"�������$�k�����7:r���B��E���>W���+b��������������~��_EEEz��'��Q#�n�Z�[�V�����SO9�JO�>�������k�^��eI��}k�����c��Od{9R�v����+.�sr��.�z����������Q���/.�7���������7��s�=�>}�H�6l��������3z��g�!W��|G��*�<�$�a��������Fx�O�@P����/�+f����h��$]rI�6n��qq�a��=��FIW^�{�_�y�_���[z���u�W8�u��I�Z���q�(�T�����rN�����V������:�:+=��a��6��
�z���`�RC�s��1�w�y���w�y:v��"�O���W�+�j�AP'�l��sg�3�rs#�a�I!0u��Y���+w|��y����"�Or�s)��@�������������^��#�z�j���K��i�&���_��/�rtz��Ifm�1G�����~��]u�U:~���?�Q�Fi��]Ja�@j=����+���I���]o�j��7��Z3~�e��,�������KII���M7���#5mZ~����s���Y�k����a��������K�t�M���_��@j	U�zM�P�(��������7����Q������,��W/&�#p=������$}���JMM�����w�^���z9:���Qok�����_������(��!��!��VU+���WR|�4v�������b,��{������$������_�g�}V[�l������@���%6J��jUN�8G��@��l6i���Wj/.�'N�.������P������aaa����$�^�Z�G��$EGG;z��21�1��py���G����d�=�1G�c�;�S<������f�\)<\���|q@����RSS��SO������#$I?�������_��(Q����]��$�����u�1���M���Q��@�g*�s�����w`7o�<���h���z��W��U+I�g�}��.��������q�FI���]��h��w~��9��1i�~�Vl��{<4��%&&��O>)w|���n��������S5i�$����u/�%�D�r�s���'����K���o����E��ode��9/)��f�Z���_��W_U�N��
�>�����W8���(����]�="B"S�~�'����?_III���P��=��W_Uy����u���*..N���:��s�����:��u\���P�S�N���o�����Io���r�s�Z�x�������y�@�A��g�-Z�={�h��9����g�}���D]p�N�����Wjj�,X��={*--MC���]�[���g�j���������K��U+����j������Nqm���WKI�\<*w���j��4h��~��*�-((PAA��q���V�UV���X����{r�������:��QI����N]��q;u��X����������?�����9�H��n�:
6L}��������3�(66V�����oh���N����^���cu���K�,X�O?�To���~��r�����:v��6n����PIRRR�'�b���DEI���d����n�������;P�,Y�-[�����v���3gj�����Z�J����+##�c�
4��=u!G����d5\+>6l���H�C��B�����g6w�[���������O?���T5l��q��K/��y������g�y�fM�:�q,((H�
��M�*�����z���{��W���5k��n�I=����$�	�������^\�O�"��^�s������Q5&I�����I����������:u�RSS��������!C�(**����V�2224x�`�/��r�����������=�}��U�]]��.����y��.����jx�@���o�����;����
��Xnn�l6��7/��c����sg��h~��'��_���7�����k���7n��V��M�V�5��]��=u1��Yt�
�����x�(��U+C�f�t�U��|R���������}
KU��.��6�?��9�n���JOOw����7�����������f����5o�<��zxx������+44��,=}�@B���O�+�2{���h�~!!!n�w��/"�������H���qc����M�6��o�����jM)**Rll�^{�5�{��:p������TZ�3����;�����l��qCd�*]���
5lxVS�|��*8X*�>dT�t���r���YWr�-��<o�k������ah��ej���z��!�^l?~��B~������oK����u�y�1�
�qU���vx�@�����C��?��bQQQ�>��sM�2E�G�v�>111
��C�J?t��Z�hQ�5qqq


-��'''����:{������]�P:�E��S���n�EG�V���E'O��w��������K�k��m��<o�[�h���=�����:-X�����l6�7����a�����C�c���W��M��}%{�w���8��#���>�{��W			��l:���e��t�M7���s�>aaa������Y��#GJ����Y�F������>}���w�UQQ��������������8�J���{�J��q��y�����y�3�[C��������aC�_x+55U�{��_�����Q�=��s��#zXX�.\��\��o��S���kW�k���{���j��1����.��"��������U�G��V�Zi�����?����7o�&M��	&�����>��'z����M*3��R�m����P;w�T���K��s������wff�[����e���G����dIRLd��.��>�������D��^��:r���x�	<xP]�t��+�egg;z�%)!!A+W��}���N�:�U�V�4i�z�!��P�l6)+K���~�QZ���U�%�6<���o��w��={����.�$}���z����$��$�$�[\��OP!���ah���Z�v�>\�7����Zl����i�����z��_|��k��l�V{�����/�E��5k�rr�{�����������������H�>y�d����0`��7o.���j�]���8{�wp��8���o5���9�YAAAz�����:��������a�i���������������{�v���z�����^�RS]/�g��&L���)�\��v�s<R�7j�H��s�'n�������������ys�s��v�����-[��p4�#����5c������W��'n	����=�����k^���Q��)�
)����:���{���URRR�=Y��;���\[��:��x��i���Z�}"[������S9
��f�L������H�>f�m��Y��r��p��B����@���y�v��!I�����kW/G�S�Od����U�}������>\���@�!�����p�gy�@���O�r�J�����P�l��C����5�������������K��?�h��%j���w�����*�sI:k;��q,�xI�'n����������$�����#>^�1Cz�]i�Zi�^�s�&M�0A'O��w�}�c�����c��}����4q�Do�@@�H��Y����j��JJJ��-�)������c���ej��+�z�j%'';�����?�����)�o������m�����,�H��c�<�2|\U���b�@�s���k�%)44TEEE^�����=--����l6i�\�Vmg�v�{.��RM�4I����Z�l)I:p�����>
8���pV�U���������*��� p���{�])�-��=���x��y�t�W())I			������C�z����g8�J;�f�@���s,���W��, �]f����!-\X��g�v���e��^�Z;w��$%''k��A^����Y��o3]�7i�D999���U���+���0Y,�l6����\�s^<���G����{�������sx��b����5x�`o��K"B"��0��e�@�������hI��E������2���������^�|VV����+���MD\�����c��!&2F����L����s���;�p���t��Q
4�9�@�����a�7%�$�[\7o�@Z$�x({Y�N�RDD�'^���s�����	����\��U���V����*�>g���Wdd��9���/��R]�tq+@�+%��3^�0��9��x��u�[���[%�{����[���9�S���5e��"�����9s���.����g��=Z�h�����9s�(66V�}��u�x;<e�j;P��U��]�V�t���k��9l���)$D*,,}�9��Y�n��
�>}�h���z��g����?z��7�t�Ro���C�+�"n@���9��-��m����m����u{q���4k��������O?���T5l��q��K/��y��8����_�w�5����s�U|�G
tuKz�}��s������G�����{�c��o��x���o�����[�xll�rs+���9�Y/����������F�,z��B����t��k$�(}����)S�_,�������.eX;��7n����i�����[��U�V^�
�'���?���a��{������aT\�?/I�'�����p�
z���t��AY,���?��)S4z�ho�@@�@���U��i�**�K>���>����>���;O			:u���?�|]r�%����{�1o�@@`�;���������������u��)u��U����vh��"p��@��[�P�����Q�?+�(�����CJ�}�����W�b�_������<�0�t�R�]�V�VQQQ������W�(��(�8K�gK�^�����kZ���h���z��W5`�5o�\���jT��l����2�9����z9*�B�@�}���,�����-���++>�^���������O���>|��CF��l�������]��s��Q�Pz�}{��C������_�;W:v��k���>������g�5�9����0���������9�@��6k@�KO����������'�������^v�k�����)�7}�t��1C�����C���9����Q�~�P6��f�4vl�C���-Yb�!o���������i�����N����bcc��cGu������!�@��w U4��"�!��/��H���������QKI���K�����7��[na�8����^C����0�C�]Y.'�^���_ca��O?�T+W�T��}�
�2�6
�@���������W%.�FB�C��0�:���j;rwx9��9�@HO����A��^��,�EJH�gP���5K>��������^y�u��IQQQ���R�^���g��$����U��Zwu��nI���!�1��u��!�������1���r������m[EFF*44����\��^||��{�9�k�N�a������W^��[���.�t���0��������l6�bp���}���4ViEZZ���u����z��3���W^�_|A�8�}���E��aYY���^Vt�}[����9��1cj��6�M~��N�>�^�zUxNAA�


����$IV�UV�������{r����+,,t��v���cL�r�	$�������n���9:P���8n������k&�%//��0\qQ\W����o��W/�9sF
4��e�t���Wx���35c��r�W�Z���H�^�*�W�!w�;r���
�����W�{�$���CN�c��
��t�����s�3�l�����z]
t�����C�}���v 5i�D999���U���+���0Y,�l6����}{m��M'N����K5f��[���"}���JMMu<���SBB�������V�2224x��rs�Q5r����������\�g^V��}��EWE����s�3���U���P�u��e��s������_����$�]�������IR������_k��9z��W������r�CCC=�����$��9'�'�.�%)$$�|�?{�!�������@��M������.��v���s��M�6JHH(��n������k��g��@��Mz���N����v%�i��1���c���M�6.
q�:u��
���D�<yR����233�r�JO�
�9l�6
t�O�l�U�sr��8)%E�����G�U{�����	i�����e�:uJ.�����=z�rrr��Q#u��I+W�����=.�5�'�����x�s*G���$��u��{���b��JO��o^r�y�VR��AN]��9�9����,���R���l6}�������K�|��7<"�3�Od����:S��<���c��-��G�	�?Q�~&=]����~�����\�W��K[�n�d�A���o�x.,,L�;w��)S��Sr�s=^�@I�����=���1���b�{����������o��v��3�#��s(�?�����i)�Z����v�-Z���!�E��D�������j;�1��j�V��8����>��E��D���s�{L�>��s���?\��.�v�:�'l6���h��1��8��3Jn��#w���P�Q�~��m����p�WS��@e����������$EDD�g��������n��%�X,9rd����m�����`�'�U���i���@]��=�����RSS�`����Siii:t�v������J���o��L���QG8��Zt�4a�����x��uf��?5�)�i�FR�E�
������d���K/i�������%I,���~�7�|S?�p���l6�|���1c����t���Z���l�v���������������m6������>��pu��V���jUN�[�8>W��={V�7o���S����4h� m������|�I�����;�TVVV��QPP�������<I��D�V���@�{x�^������E��3���PEE�z��������|**���
~��C��s7w��Oiw�\����+�������:��ys�����k6l��7�xC��ms�5f���3f�;�j�*EFF�se222<v�@�������o�_�P�&g���Qef�#�c�����Z����q ��S��{��yfs�����H���*�|����:y��n��V-\�P111N]3u�T���:���)!!AC�QTT��1Y�Veddh���


u�~�$Ps�l�E���:p��8d���R�U��b�U+i���
��y�s�3�����:Vi�k|�@���Qpp�:T���C���E�r����G������_�8V��q�!!!��k���m[����p�����Whh�G?Xz�~�$�r��.�pC����/�%��9s�����
��y�s�3�l��7`C����f-,,L��w��5k�����f���������w����[m������+����m�6%$$�f��K�Y��2��������7E�D(&��Q�P��A����T�3F=z��E]���4�>}���������U+��9S���C��7n,I�����U�+2{�}k5Vi�5��s������,I���a5�����_�#G���'������K�X���p\vv���|��pY���W�7�8�U��s��\��j�.�,�%i���?~|��effVy����=�a6�Tf����y6�'��3�@M�������s�]�n�������L\�<���-�@-���s�?�XJKs�z��uOKcx;��!�j:P������b�����U��=iP�(����.]s�k[���-�'m�h_H..�>���s\cf�v���
t�����ys),L���F���*Y�������G����K�`H;o�@j��}�Y��yj5�
t����9��`���w�����^<g�wo��9g�v��9�|:���^v���J;���s��pSe��;[�O�,]y%����p��U�%)!�s��@�`v�����	�1�]f�9_E����U��5oNq��<�����%����W�>��S�����<,8L���+���1f�v��0�f������s��9�<�=�l��_P�&T��Zu���������6j�E���M��j��e���xi�RVmP1z�'�lRf�4vl���EGK|`��q�}!��8�9�ViH(�'�2���1{!&��_��Pgyb�v�'�@5���W�k^��������9�t�
6������\b5�m��������aC���j�����k���B�b�s��t�
YY����6j��u�t�����/Taa�y�
2D����������V
���@*a�Ik�8>�����+J=^�x�bcc�y�f]r�%^�
�""$B)�)��:P3������s�Q��N�8!I��������8���I��V��V���_|O�+�x#w�'�u�������;����+��y1�I���k���8��~��g�s�3�����s
t�W�+�V�z�����"M�<Y}��Q�*<g����1cF���V�Rdd��b������Mm����#�c���kpsw�*'��Bk�r�]�k"<���3������g6w���n�.:P�+��i_�P8�f�u���j�����aC��L�:U�����yyyJHH��!C�vV�U<x�BCC��_ �����1?�{B��]�}��������������y��=��<wsW<
�,
t,���\N�}������_v~X;C��j����'�|����+>>������^�xhh�G?Xz�~���r�}"[^���Vj!j��������G��C��3�;w�M���T���`{������OgH;T�0M�0A��-Sff���i����C�Od+7?W��#w����U��U�8��1w�8��C�)��b��{��}�]}���j���<(Ij����������M�'��~^{�z�%)9&Y���y(*��(��1���@�^y�IR���K_�h�n����^��s�(YY�m�V���s��E���sA��M99������.eQ8����Kq.��f�2w�����	��P��C�=-"$B1�1�/�
t�Y%�Q��G�~��o/�sNq@�jjH;�����*�F�U�9�551�="$B)�)�:����Q�N�}�����9s��]��T��S�n�6{�4n��q�}H|\�}+5z��]��d��3l6i�\s���7����2[��Z@�9�Q��Npw�y\�g����~G��gv����*�))��T�s�:���9����	f�=O�IV��n5�%
t���,���Y�����<"$B1�15�/
t�������������c�v<��}����@�����C��;w�li�
r��!��
t�%gWm/^���03��
t�
��>�����s���"px��9��Q��/����E��,g����@��3��9s��
t�4���7oNq�'9{D[nUHHs���Q������<.���h�Odk��q�~o5}�=�Q��g�l��5�]S�j{JJ��@]Wr�����j�^���9�C���l�E���Z�9���O�����`:|��&�[g�o\����
�U�p���wy;����?_III���P��=��W_Uz�������&M��I�&4hP������KII���!��?� �����'Kk�J{�R��m�9�|�����Wjj�,X��={*--MC���]�[����L�x������"""����k��!������U+/����6j���@�9��9��>����K/i�������u���k�������o�Y���������.]������������"�qu�1�:���=�=�xB��lm���-9[Lo�V<��[\7�sp�����={V�7o���S����4h� m����{����j�*::�������Fm�@��]�Z�>W�����f��y����7o�\;w�t�=��Z�l�A�U�|AA�


����$IV�UV��}>������l6i�� I�W���V���/.�-��=���{��y������X�9��9>W��������%K�������
��9s�f��Q���U���X222<v��f��8��zG=Z����t��_k�����.�g�<r��g������{8�u%�97;��9�P3|�@���Qpp�:T���C���E�*�}����s�i������S��M�:U�����yyyJHH��!C����7###C�Vhh����k�-����]�w/��e�UWu���Fb�w���G��C��s7w���gxbH{DH�RS(���\�����k��59r�$9|?~|�����z��g�r�J������Wxxx������`����;�M�����g���<Y��J)%���`����I���G��C��3�;�
W����o�C���1�����Njj����=z���.RZZ�N�>��o�]�4z�h�j�J3g��$=���z��'����*))I�$5h�@
4������������ �����9O���y;��|�@����u��=��:x���t��+V8����VP��;����+:{������R��6m��O�^���f�9�:����
f�v���9������$�?��!�����������)g�J����>��^jP��&�Q�T�)�9��R�V��#�_c����W��.V��Ol�j	U�zM=�*>���ffX��b�z����*����m�
����(�
txT�J�c��>�=>^z�E���s�6j��SC��c��-���V�r�r<�:q����KII��A��c�_-�^-��+]uC�0�C�#B"�����afH�d��p�4p��qQ��c�.���v�}���(��6�������y�f���su��������E�7oVNN��-[��#Gz;�:��C���w��f������_��k��~
�9�=�O�V���5�|o�R�ybH;�w������=����+�/X ���L\�6l��
��0���a�f�����s��0�����x)-��s��


TPP�x���'I�Z��Z�n������/�>��:���|��[W���b��$5��Tq�q�������?���{��y�����S��ef��GGK| ��/�Xh?4s�L��1���U�V)22�c������{y���=n����3W9����r����^��u%w�B��#w�!���]~~�[�K���:��xH{���(i���JMMu<���SBB�����(��o�Z������+44���y���[��_�+_��J�u-w����G��C��s7w�����@����9gH;�:���
/w<44��,=}?o		q���������y�3������g6w���N13�|�li����.w�:g5�����&efJc�:_�[,��s�sL�N����������m��)::Z���������Y��:*��6jii�����k������������{)*�ev���a�f����b5�Y�����j�R��.1��:J13��s�p��C�%�����@���!�s��$w��K�Vjx:$����s|C���Q�C6�����tA���>O�9�?
�g�Is��>�=:Z���z�p�'���
���6jJ�L\�s�KbQ8�(���&eeIl��*���X�
�bv�v�!�x��*B� �]��!�x��*C�X���>���P��qfWi�����	����pzfv���b�9�8�}l�pz�����4�s�*�c�6jgP��A�9�{<�c��s<�u��9��'KW^)���s�;�.Ws� �P��!f��'$�c����03��U��O��^s� 0Q��9�M�����u�8g�v<�9�O�@�cf���J;���*�s��~��6j���{jb_s��$
t�cfH{1���>O����v@Y�>�f���������]�B�9������$�P�J�@�af�����s|=���P��(�s��b�9���6j,�����=������H���s�;jbQ8*B��cl6i�\������/\(
���T�X�U���@�!��s��v���!��9���G�;��!�x�'���J;�
t���s���Yf���cp���;��!���U�������9����������������Vi�

t/13�|�li�
r<����	���f�23�����������������F
�	�������E���(��O
igQ8��Q��0�M���>��^d��E��,O
igQ8��Q���e�,��~�+�3��8r���������|z
��)N/�lzos����'�5n�8Y���u��j���l���e���]L�s�pG�E���.��^q.1�P���@e������$EDD�g�������<��?�y�����u��Q��/��H��.%%I�]�S��$Y\�G|��t)s������S����Zwu����c���dH;�6�d�����+55U��M��-[��sg
:T�����7��o��w���[�j���9r��o�^k1�knv�yt��z��w/�9�����{���{{���|�fm�{�v����v@������^���cu����������7�|��������.�L<�������SO�[�n�7o^��k���O3;��b�.�dX;�np�-�E�C���u�8�
���~��Ym��YS�Nu

��A��i��
���i�RSSK:t�>���
�/((PAA��q^^�$�j��ju}���u����R����Y�l��rC&^��)������;��{��y���s�j[��v����D��EG�Qh#��om���{��y��=��<o��>W�����f��y����7o�\;w�����Vx���+<����1cF���V�Rdd��1�_�JR'��w�_~�]t�A��QK^�2��222��_#��;��?���.??�������������=._#I�%����xIRTH���]�U{��|���C��#w�!�y�]���6L�:�T�{^^�4d�EEE�|���-z�%�����f�����ZKj��k�uV�U<x�BCC���!��;��?���]qo�?�t����V����a�
������a��v�3��������v��
������C��:tH-Z����-Z�t~xx�����

5�M0�^x8P�<��h�����-
��������?���{��yfs���v�-�t��z;�Q-|2�����!��;��?���.��"qaaa������Y�8VTT�5k��W�^^��W�R�K�!	���i����9��[����"p�@c�-�Vi����MMM��1c��G]t�EJKK����u���K�F��V�Zi�����I�&�_�~�5k�F��%K������^{��Z�y�(����&��j->^JKc�4@`��-�I1�1���r�����$�P�|�O��_��9�'�xBT�.]�b�
�b3���

����w��z��w��c���GQ�v���G�C����Q��WJk�����i��.0 �^s@���-�I���k�.���J����a�����1�=&2���s|�@�����k���>���Y����^�k������^p��������_�����UU[^�%:
p�����um������ss�D��
t|:>�@���@�P��(����
t|@����aH����<r?�����|���)44�#���=��<r��g���+n������v�w�;��?���{��y�n�)�%�<yR������H0����j�������e��3�.[���+w������/��a���X,n�///O			������<a� w�!��;��?����a:y��Z�l����1s�v�w�;��?���{��y�n��A����x��7**�&�;��?���{��y�������]�=��=��<r��g�������v�:>�����k��i
�v(~������G��C��#w5��G��C��#w�!�y;w,��@���@�P��4�|%%%)""B={��W_}U��~���;�<EDD�c��Z�|y-E�{\��������&M��I�&4hP����\��+�d�Y,�9�f�a���������{���p�{����u!iiij����������w�}:s�L-E�;��_��/�\-[���b�G}T�5������������?�A�/��8���y����]6�v�=����|�l�eK�,1����7�|�������c��76:T�����l������o<��cFhh������r���j�n��&c������[�;v��v���Q#����o-G�\�_��{��Z�2RRR�+���v��1����������1|�pc��
���{���Lc��m��op5����n���;���{��+Wqqq�}��W��{�����G}�HOO7$��-����~�����4RSS������;w�l�X��v�C����.��v�<�e��.����2�	]t�q���:�l6�e�����3+<����3F�Q�X��=�?��O5�/r5we
64�z���
����_aa���wo���_7���\��+��b�s�9���gk+D��j����^��K/-u,55����O������ ���\pA�c�_�1t������.�G���e�h��C����.3��Eg������5h� ���� 
4H�6m���M�6�:_��Z��u�������/�������
�g����O>���X�y����O2�����������^5o�\:t���>+��V[a�3�����6o��n��O?i���>|x����h3\C�l��{h���]v�r���6#�F�Z�����f��y����7o�\;w�����Vx���k,N_d&we=��Cj��e�$��L�6l��7�xC��m��}������O������o�Y��/����5n�8Y�VM�6�6��f�w�M7)77W}���a*,,�=���Gy�6B�k��yyy����T�^=/E��h���]v��y����]�]��.�����s�i��%Z�l�"""���;y��n��V-\�P111���)66V�����w�����^�>��,X����Bff��}�Y������e�������������vh<�v�5����]v�����E111
��C�J?t��Z�hQ�5-Z�p����L������z����z�ju���&��Y��o��=��o�.��r����"IRHH�v����m��l�>���^\\�BCC�8���������
���}���=������[u�]wI�:v����O�������>�� ~?\������(z�+@�l��{h���]v�r���v���������{w�Y��q���Hk��Q�^�*��W�^�������J�����N�^x�=��SZ�b�z��Q��$W�w�y���o���m����

0@��mSBBBm��Uf~�������w;><I�?������� ��_~~~����C�}MT�6�5����.��v�<�e��.��Zo3jd��:n��%Fxx��x�b����7���n�q�����
�0�[o��x�������FHH����/;v�0�M���������{�3�.]j���8��<y�[o��\�_Y��Z������66lh�?���k���'������O?����U��o��iF��
���{������U�Vm��5���:o��9y���u�Vc����$���^2�n�j�����a��?l�z������sy���;v���g��j�.�G���e�h��C�l�����&��;�HLL4�����.�����/�����3fL��?����s�5����.�����Ok9b��J�Z�nmH*�g��i���p�g��@� `��n���F��=���p��s�1�y�����������j���O7��mkDDD			��q��_������k�V��Xq���c�����5]�t1����s�9�X�hQ���oh���]v��y����]6���e�a0�oc:>�@���@�P��(����
t|:�r������'{;eff�b������0m���������eKY,}��G.��0����:��s�V�Z��g��|�|�2�9��.S��	�������O�s���?��{L�4I����^|�E���S���?t�Ey0J(�vu����!����a�4l��J�/((���>���{O��W����������;v��W^������}{IR�6mj#t�_o��AP���M�2E�Z�R�����gOeff:�_�x�7n��+W*99Y
4�e�]����9����8q�7n��M������1c4r�HI�m���u��i��9�X,�X,��o�����7�G����T����k��Zz�@�?~�6m��%K���o����^��.�L?���$������9�}��'j������t�]w���c^��7�.5���2:�*U���$�����_�����i��������)S�?���z��w�h�"}������+5�g��9������������(!!�����>�Y�f�����BBBt�w��{jZvv�-Z�?�P)))j����L���}�j��E���~�I?���>��C��o�����y�f]s�5^��7�.5��e���T�R���j���$i��)Z�b�-Z�g�}V�d�Z�`��m�V�����O>�����s5u�T]u�U��y��i�����5j���0EFF�E���x��g��_?I��?�#F���3�����7��o��V6�M��{n��j���$���H�����8��7�P����k�.��:u�2P�|�]�@P)g������H��I�������%I'N���C�J-����������8:u�T���t��a%&&���r��)k���
.�\�
$��CBBJ�;LNN�d��N��e�f�B�L��R��'%I������X,2�cq����b�$�?D��k����l:|��RRR*<�O�>*,,��={���IR���k-V�G��,_h�)�T�������Q#5o�\_��.��I��f��-[��K�yaaa��l��)�N����������m��)::Z��{�n��f�=Z�f�R��]u���Y�F�:u��#4h� u��Mw�q����TTT�{��W�.���n�]����2���T�������w�^}��W�9s�>��S��3a���9S��v���I�&��_u��]�������_j��}����7��3����k������$)55U]�v�O<!IZ�h�F�����_������#���_;�������bbbt�%�h��JNN��%K���x�2�>_o��AP�E����������(&&F_|����?:}��zH��������[C�-5<o��)3f��?�|���o��woM��������������1c�f��Q�9-[����������]���������pBQQ����u�u������v84�e�w��������Z�j���������7O{���M7����8����b:������/T�>}����j����-)@��]|C�����(����
t|:>�@���@�P��(����
t|:>�����r�����IEND�B`�
int_sort_bench6.pngimage/png; name=int_sort_bench6.pngDownload
main.pytext/x-python; charset=US-ASCII; name=main.pyDownload
#9Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: Stepan Neretin (#6)
Re: Sort functions with specialized comparators

On 15 Jul 2024, at 12:52, Stepan Neretin <sncfmgg@gmail.com> wrote:

I run benchmark with my patches:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.609 ms
initial connection time = 24.080 ms
tps = 6214.244789 (without initial connection time)

and without:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.731 ms
initial connection time = 15.177 ms
tps = 5776.173285 (without initial connection time)

tps with my patches increase. What do you think?

Hi Stepan!

Thank you for implementing specialized sorting and doing this benchmarks.
I believe it's a possible direction for good improvement.
However, I doubt in correctness of your benchmarks.
Increasing TPC-B performance from 5776 TPS to 6214 TPS seems too good to be true.

Best regards, Andrey Borodin.

#10Stepan Neretin
sncfmgg@gmail.com
In reply to: Andrey M. Borodin (#9)
Re: Sort functions with specialized comparators

On Tue, Jul 16, 2024 at 1:47 AM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

On 15 Jul 2024, at 12:52, Stepan Neretin <sncfmgg@gmail.com> wrote:

I run benchmark with my patches:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.609 ms
initial connection time = 24.080 ms
tps = 6214.244789 (without initial connection time)

and without:
./pgbench -c 10 -j2 -t1000 -d postgres

pgbench (18devel)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 2
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 10000/10000
number of failed transactions: 0 (0.000%)
latency average = 1.731 ms
initial connection time = 15.177 ms
tps = 5776.173285 (without initial connection time)

tps with my patches increase. What do you think?

Hi Stepan!

Thank you for implementing specialized sorting and doing this benchmarks.
I believe it's a possible direction for good improvement.
However, I doubt in correctness of your benchmarks.
Increasing TPC-B performance from 5776 TPS to 6214 TPS seems too good to
be true.

Best regards, Andrey Borodin.

Yes... I agree.. Very strange.. I restarted the tps measurement and see
this:

tps = 14291.893460 (without initial connection time) not patched
tps = 14669.624075 (without initial connection time) patched

What do you think about these measurements?
Best regards, Stepan Neretin.

#11Stepan Neretin
sndcppg@gmail.com
In reply to: Stepan Neretin (#10)
10 attachment(s)
Re: Sort functions with specialized comparators

Hi! I rebase, clean and some refactor my patches.

Best regards, Stepan Neretin.

Attachments:

v2-0001-Use-specialized-sort-facilities.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Use-specialized-sort-facilities.patchDownload
From f88cbb80e478d5ac3f23945b4ba0ee881f0d9cd4 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sun, 8 Sep 2024 15:43:39 +0700
Subject: [PATCH v2 01/10] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 12 ------------
 contrib/intarray/_int_gist.c |  2 +-
 contrib/intarray/_int_op.c   | 19 +++++++++----------
 contrib/intarray/_int_tool.c | 12 ------------
 src/include/port.h           |  2 ++
 src/port/qsort.c             | 35 +++++++++++++++++++++++++++++++++++
 6 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..5225c9090a 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -176,16 +176,4 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
-/* sort, either ascending or descending */
-#define QSORT(a, direction) \
-	do { \
-		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
-	} while(0)
-
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index a09b7fa812..d39e40c66a 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -150,7 +150,7 @@ g_int_union(PG_FUNCTION_ARGS)
 		ptr += nel;
 	}
 
-	QSORT(res, 1);
+	sort_int32_asc(ARRPTR(res), ARRNELEMS(res));
 	res = _int_unique(res);
 	*size = VARSIZE(res);
 	PG_RETURN_POINTER(res);
diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c
index 5b164f6788..34d3aa183f 100644
--- a/contrib/intarray/_int_op.c
+++ b/contrib/intarray/_int_op.c
@@ -198,7 +198,6 @@ sort(PG_FUNCTION_ARGS)
 	text	   *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_PP(1) : NULL;
 	int32		dc = (dirstr) ? VARSIZE_ANY_EXHDR(dirstr) : 0;
 	char	   *d = (dirstr) ? VARDATA_ANY(dirstr) : NULL;
-	int			dir = -1;
 
 	CHECKARRVALID(a);
 	if (ARRNELEMS(a) < 2)
@@ -208,18 +207,18 @@ sort(PG_FUNCTION_ARGS)
 						   && (d[0] == 'A' || d[0] == 'a')
 						   && (d[1] == 'S' || d[1] == 's')
 						   && (d[2] == 'C' || d[2] == 'c')))
-		dir = 1;
+		sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	else if (dc == 4
 			 && (d[0] == 'D' || d[0] == 'd')
 			 && (d[1] == 'E' || d[1] == 'e')
 			 && (d[2] == 'S' || d[2] == 's')
 			 && (d[3] == 'C' || d[3] == 'c'))
-		dir = 0;
-	if (dir == -1)
+		sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
+	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("second parameter must be \"ASC\" or \"DESC\"")));
-	QSORT(a, dir);
+
 	PG_RETURN_POINTER(a);
 }
 
@@ -229,7 +228,7 @@ sort_asc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -239,7 +238,7 @@ sort_desc(PG_FUNCTION_ARGS)
 	ArrayType  *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
 
 	CHECKARRVALID(a);
-	QSORT(a, 0);
+	sort_int32_desc(ARRPTR(a), ARRNELEMS(a));
 	PG_RETURN_POINTER(a);
 }
 
@@ -381,7 +380,7 @@ intset_union_elem(PG_FUNCTION_ARGS)
 
 	result = intarray_add_elem(a, PG_GETARG_INT32(1));
 	PG_FREE_IF_COPY(a, 0);
-	QSORT(result, 1);
+	sort_int32_asc(ARRPTR(result), ARRNELEMS(result));
 	PG_RETURN_POINTER(_int_unique(result));
 }
 
@@ -403,10 +402,10 @@ intset_subtract(PG_FUNCTION_ARGS)
 	CHECKARRVALID(a);
 	CHECKARRVALID(b);
 
-	QSORT(a, 1);
+	sort_int32_asc(ARRPTR(a), ARRNELEMS(a));
 	a = _int_unique(a);
 	ca = ARRNELEMS(a);
-	QSORT(b, 1);
+	sort_int32_asc(ARRPTR(b), ARRNELEMS(b));
 	b = _int_unique(b);
 	cb = ARRNELEMS(b);
 	result = new_intArrayType(ca);
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..e83c6aadc6 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -393,15 +393,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
diff --git a/src/include/port.h b/src/include/port.h
index ba9ab0d34f..b82f969fc5 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -443,6 +443,8 @@ extern char *strsep(char **stringp, const char *delim);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32_asc(int32 *base, size_t nel);
+extern void sort_int32_desc(int32 *base, size_t nel);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 7879e6cd56..5175c8a6dd 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -20,3 +20,38 @@ pg_qsort_strcmp(const void *a, const void *b)
 {
 	return strcmp(*(const char *const *) a, *(const char *const *) b);
 }
+
+static inline int
+sort_int32_asc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return -1;
+	if (*a > *b)
+		return 1;
+	return 0;
+}
+
+#define ST_SORT sort_int32_asc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_asc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+static inline int
+sort_int32_desc_cmp(int32* a, int32* b)
+{
+	if (*a < *b)
+		return 1;
+	if (*a > *b)
+		return -1;
+	return 0;
+}
+#define ST_SORT sort_int32_desc
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE sort_int32_desc_cmp
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
-- 
2.43.0

v2-0004-Optimized-Integer-List-Sorting-by-Using-Template-.patchtext/x-patch; charset=US-ASCII; name=v2-0004-Optimized-Integer-List-Sorting-by-Using-Template-.patchDownload
From 337cb90eb37c5982e2d7f2dc6ab70fbb500c6010 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:00 +0700
Subject: [PATCH v2 04/10] Optimized Integer List Sorting by Using Template
 Sorting Algorithm

Optimized the sorting of lists containing integers by utilizing a custom sort template.
This enhancement introduces a specialized sorting function sort_list_int defined through macros and included from sort_template.h.
The new function improves performance by directly comparing integers and sorting the list in-place.

Changes:
- Defined ST_SORT, ST_ELEMENT_TYPE, ST_COMPARE, ST_SCOPE, and ST_DEFINE macros for sort_list_int.
- Included sort_template.h to leverage the template-based sorting mechanism.
- Implemented list_int_sort function to sort lists with int type.

This optimization is expected to enhance the efficiency of sorting operations for integer lists, leading to overall performance improvements in systems that manage large lists of integers.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index b2165c6284..197ce1b8f4 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1720,4 +1720,18 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
  */
 void list_oid_sort(List *data){
    sort_list_oids(list_head(data), list_length(data));
+}
+
+#define ST_SORT sort_list_ints
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_int_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with int type optimization.
+ */
+void list_int_sort(List *data){
+   sort_list_ints(list_head(data), list_length(data));
 }
\ No newline at end of file
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 88aa1ebea9..7757b1cdf0 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -680,6 +680,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
 extern void list_oid_sort(List *list);
+extern void list_int_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.43.0

v2-0005-Refactor-Grouping-Sets-Sorting-to-Use-list_int_so.patchtext/x-patch; charset=US-ASCII; name=v2-0005-Refactor-Grouping-Sets-Sorting-to-Use-list_int_so.patchDownload
From 80fbedfc64a329b506440a7374b183ad1d469748 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:04 +0700
Subject: [PATCH v2 05/10] Refactor Grouping Sets Sorting to Use list_int_sort

Refactored the sorting logic for grouping sets in the expand_grouping_sets function by replacing the use of list_sort with list_int_cmp to the optimized list_int_sort function for sorting individual group sets.
---
 src/backend/parser/parse_agg.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index bee7d8346a..f949b81e84 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1870,7 +1870,7 @@ expand_grouping_sets(List *groupingSets, bool groupDistinct, int limit)
 
 		/* Sort each groupset individually */
 		foreach(cell, result)
-			list_sort(lfirst(cell), list_int_cmp);
+			list_int_sort(lfirst(cell));
 
 		/* Now sort the list of groupsets by length and contents */
 		list_sort(result, cmp_list_len_contents_asc);
-- 
2.43.0

v2-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchtext/x-patch; charset=US-ASCII; name=v2-0003-Enhanced-Sorting-Efficiency-for-Oid-Lists.patchDownload
From c068e66cb7577aaf7a668b71f17ac95ff35b7736 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:43:56 +0700
Subject: [PATCH v2 03/10] Enhanced Sorting Efficiency for Oid Lists

- Replaced `list_sort(result, list_oid_cmp)` with `list_oid_sort(result)` for optimized OID sorting.
- Updated the following files:
  - `src/backend/catalog/heap.c`
  - `src/backend/catalog/pg_publication.c`
  - `src/backend/utils/cache/relcache.c`
---
 src/backend/catalog/heap.c           | 2 +-
 src/backend/catalog/pg_publication.c | 2 +-
 src/backend/utils/cache/relcache.c   | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 01b43cc6a8..368044459a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3311,7 +3311,7 @@ restart:
 	list_free(oids);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 7fe5fe2b86..31e1694cfe 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -728,7 +728,7 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
 	table_close(pubrelsrel, AccessShareLock);
 
 	/* Now sort and de-duplicate the result list */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 	list_deduplicate_oid(result);
 
 	return result;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 63efc55f09..1220e6ca9a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4874,7 +4874,7 @@ RelationGetIndexList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
@@ -4966,7 +4966,7 @@ RelationGetStatExtList(Relation relation)
 	table_close(indrel, AccessShareLock);
 
 	/* Sort the result list into OID order, per API spec. */
-	list_sort(result, list_oid_cmp);
+	list_oid_sort(result);
 
 	/* Now save a copy of the completed list in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-- 
2.43.0

v2-0002-Optimized-Oid-List-Sorting-by-using-template-sort.patchtext/x-patch; charset=US-ASCII; name=v2-0002-Optimized-Oid-List-Sorting-by-using-template-sort.patchDownload
From 0189f0997747c7ed9e0a834e436cec72b14fdb0f Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:43:52 +0700
Subject: [PATCH v2 02/10] Optimized Oid List Sorting by using template sorting
 algorithm

Optimized the sorting of lists containing Oids by utilizing a custom sort template.
This enhancement introduces a specialized sorting function sort_list_oids defined through macros and included from sort_template.h.
The new function improves performance by directly comparing Oids and sorting the list in-place.

Changes:
- Defined ST_SORT, ST_ELEMENT_TYPE, ST_COMPARE, ST_SCOPE, and ST_DEFINE macros for sort_list_oids.
- Included sort_template.h to leverage the template-based sorting mechanism.
- Implemented list_oid_sort function to sort lists with Oid type.

This optimization is expected to provide better sorting efficiency for lists containing Oids, contributing to overall system performance improvements.
---
 src/backend/nodes/list.c    | 14 ++++++++++++++
 src/include/nodes/pg_list.h |  1 +
 2 files changed, 15 insertions(+)

diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index e2615ab105..b2165c6284 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -1707,3 +1707,17 @@ list_oid_cmp(const ListCell *p1, const ListCell *p2)
 
 	return pg_cmp_u32(v1, v2);
 }
+
+#define ST_SORT sort_list_oids
+#define ST_ELEMENT_TYPE ListCell
+#define ST_COMPARE(a, b) list_oid_cmp(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+/*
+ * Sort list with Oid type optimization.
+ */
+void list_oid_sort(List *data){
+   sort_list_oids(list_head(data), list_length(data));
+}
\ No newline at end of file
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 52df93759f..88aa1ebea9 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -679,6 +679,7 @@ extern pg_nodiscard List *list_copy_deep(const List *oldlist);
 
 typedef int (*list_sort_comparator) (const ListCell *a, const ListCell *b);
 extern void list_sort(List *list, list_sort_comparator cmp);
+extern void list_oid_sort(List *list);
 
 extern int	list_int_cmp(const ListCell *p1, const ListCell *p2);
 extern int	list_oid_cmp(const ListCell *p1, const ListCell *p2);
-- 
2.43.0

v2-0006-Optimize-int16-Array-Sorting-in-CreateStatistics.patchtext/x-patch; charset=US-ASCII; name=v2-0006-Optimize-int16-Array-Sorting-in-CreateStatistics.patchDownload
From f248a7f9ca8c85426f89de29caac0135b374c107 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:07 +0700
Subject: [PATCH v2 06/10] Optimize int16 Array Sorting in CreateStatistics

Optimized the sorting of int16 arrays in the CreateStatistics function by replacing qsort with the custom sort_int_16_arr function generated via the template-based sorting mechanism.

Changes:
- Introduced a new sort_int_16_arr function using the sorting template.
- Defined macros ST_SORT, ST_ELEMENT_TYPE, ST_COMPARE, ST_SCOPE, and ST_DEFINE for sort_int_16_arr.
- Replaced qsort(attnums, nattnums, sizeof(int16), compare_int16) with sort_int_16_arr(attnums, nattnums).
---
 src/backend/commands/statscmds.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..23952e83fd 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -55,6 +55,13 @@ compare_int16(const void *a, const void *b)
 	return (av - bv);
 }
 
+#define ST_SORT sort_int_16_arr
+#define ST_ELEMENT_TYPE int16
+#define ST_COMPARE(a, b) compare_int16(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 /*
  *		CREATE STATISTICS
  */
@@ -404,7 +411,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	 * it does not hurt (it does not matter for the contents, unlike for
 	 * indexes, for example).
 	 */
-	qsort(attnums, nattnums, sizeof(int16), compare_int16);
+	sort_int_16_arr(attnums, nattnums);
 
 	/*
 	 * Check for duplicates in the list of columns. The attnums are sorted so
-- 
2.43.0

v2-0007-Introduce-a-sorting-template-for-float8-arrays-in.patchtext/x-patch; charset=US-ASCII; name=v2-0007-Introduce-a-sorting-template-for-float8-arrays-in.patchDownload
From 8e46856468d631e65efe8b06c8fb09bd1eeb0cfa Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:10 +0700
Subject: [PATCH v2 07/10] Introduce a sorting template for float8 arrays in
 geo_spgist.c to boost performance.

---
 src/backend/utils/adt/geo_spgist.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 51378dca5b..fa0a753fa4 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -100,6 +100,17 @@ compareDoubles(const void *a, const void *b)
 	return (x > y) ? 1 : -1;
 }
 
+ /*
++ * Instantiating a Sorting Template for float8 Arrays
++ * enhancing speed performance.
++ */
+#define ST_SORT sort_float8_arr
+#define ST_ELEMENT_TYPE float8
+#define ST_COMPARE(a, b) compareDoubles(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 typedef struct
 {
 	float8		low;
-- 
2.43.0

v2-0009-Add-extenstion-to-bench-perfomance-improvements.patchtext/x-patch; charset=US-ASCII; name=v2-0009-Add-extenstion-to-bench-perfomance-improvements.patchDownload
From 1676025cf6c95996ed4f0d44520ef9d06d0d7b6c Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:19 +0700
Subject: [PATCH v2 09/10] Add extenstion to bench perfomance improvements.

---
 contrib/bench_sort_improvements/Makefile      |  20 ++
 contrib/bench_sort_improvements/bench.c       | 230 ++++++++++++++++++
 .../bench_sort_improvements--1.0.sql          |   7 +
 .../bench_sort_improvements.control           |   5 +
 4 files changed, 262 insertions(+)
 create mode 100644 contrib/bench_sort_improvements/Makefile
 create mode 100644 contrib/bench_sort_improvements/bench.c
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
 create mode 100644 contrib/bench_sort_improvements/bench_sort_improvements.control

diff --git a/contrib/bench_sort_improvements/Makefile b/contrib/bench_sort_improvements/Makefile
new file mode 100644
index 0000000000..46458ee76c
--- /dev/null
+++ b/contrib/bench_sort_improvements/Makefile
@@ -0,0 +1,20 @@
+MODULE_big = bench_sort_improvements
+
+OBJS = \
+	$(WIN32RES) \
+	bench.o
+
+EXTENSION = bench_sort_improvements
+
+DATA = bench_sort_improvements--1.0.sql
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/bench_sort_improvements
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/bench_sort_improvements/bench.c b/contrib/bench_sort_improvements/bench.c
new file mode 100644
index 0000000000..a4088783c6
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench.c
@@ -0,0 +1,230 @@
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "nodes/pg_list.h"
+#include <limits.h>
+#include <time.h>
+
+
+PG_MODULE_MAGIC;
+
+Datum bench_int_sort(PG_FUNCTION_ARGS);
+Datum bench_int16_sort(PG_FUNCTION_ARGS);
+Datum bench_float8_sort(PG_FUNCTION_ARGS);
+Datum bench_oid_sort(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(bench_oid_sort);
+PG_FUNCTION_INFO_V1(bench_int_sort);
+PG_FUNCTION_INFO_V1(bench_int16_sort);
+PG_FUNCTION_INFO_V1(bench_float8_sort);
+
+Datum
+bench_oid_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    Oid random_oid;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_oid = (Oid) (random() % (UINT_MAX - 1) + 1); 
+        list_first = lappend_oid(list_first, random_oid);
+        list_second = lappend_oid(list_second, random_oid);
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_oid_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_oid_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+Datum
+bench_int_sort(PG_FUNCTION_ARGS)
+{
+    int32 list_size = PG_GETARG_INT32(0);
+    List *list_first = NIL;
+    List *list_second = NIL;
+    int random_int;
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < list_size; i++)
+    {
+        random_int = rand(); 
+        list_first = lappend_int(list_first, random_int); 
+        list_second = lappend_int(list_second, random_int); 
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_sort(list_first, list_oid_cmp);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    list_int_sort(list_second);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    list_free(list_first);
+    list_free(list_second);
+    
+    result_message = psprintf("Time taken by list_sort: %ld ns, Time taken by list_int_sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+/*
+stupid copy for tests
+*/
+static int
+compare_int16(const void *a, const void *b)
+{
+	int			av = *(const int16 *) a;
+	int			bv = *(const int16 *) b;
+
+	/* this can't overflow if int is wider than int16 */
+	return (av - bv);
+}
+
+Datum
+bench_int16_sort(PG_FUNCTION_ARGS)
+{
+    int32 arr_size = PG_GETARG_INT32(0);
+    int16 *arr_first = (int16 *)palloc(arr_size * sizeof(int16));
+    int16 *arr_second = (int16 *)palloc(arr_size * sizeof(int16));
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < arr_size; i++)
+    {
+        arr_first[i] = (int16)rand();
+        arr_second[i] = (int16)rand();
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    qsort(arr_first, arr_size, sizeof(int16), compare_int16);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    sort_int_16_arr(arr_second, arr_size);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    pfree(arr_first);
+    pfree(arr_second);
+    
+    result_message = psprintf("Time taken by usual sort: %ld ns, Time taken by optimized sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+double inline rand_double() {
+    return (double)rand() / RAND_MAX; 
+}
+
+/*
+stupid copy for tests
+*/
+static int
+compareDoubles(const void *a, const void *b)
+{
+	float8		x = *(float8 *) a;
+	float8		y = *(float8 *) b;
+
+	if (x == y)
+		return 0;
+	return (x > y) ? 1 : -1;
+}
+
+/*
+stupid copy for tests
+*/
+#define ST_SORT sort_float8_arr
+#define ST_ELEMENT_TYPE float8
+#define ST_COMPARE(a, b) compareDoubles(a, b)
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
+Datum
+bench_float8_sort(PG_FUNCTION_ARGS)
+{
+    int32 arr_size = PG_GETARG_INT32(0);
+    float8 *arr_first = (float8 *)palloc(arr_size * sizeof(float8));
+    float8 *arr_second = (float8 *)palloc(arr_size * sizeof(float8));
+    struct timespec start, end;
+    long time_spent_first;
+    long time_spent_second;
+    double percentage_difference = 0.0;
+    char *result_message;
+
+    for (int i = 0; i < arr_size; i++)
+    {
+        arr_first[i] = rand_double();
+        arr_second[i] = rand_double();
+    }
+
+    // Timing the first sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    qsort(arr_first, arr_size, sizeof(float8), compareDoubles);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_first = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    // Timing the second sort function
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    sort_float8_arr(arr_second, arr_size);
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    time_spent_second = (end.tv_sec - start.tv_sec) * 1000000000L + (end.tv_nsec - start.tv_nsec);
+
+    percentage_difference = ((double)(time_spent_first - time_spent_second) / time_spent_first) * 100.0;
+
+    pfree(arr_first);
+    pfree(arr_second);
+    
+    result_message = psprintf("Time taken by usual sort: %ld ns, Time taken by optimized sort: %ld ns, Percentage difference: %.2f%%", 
+                              time_spent_first, time_spent_second, percentage_difference);
+    PG_RETURN_TEXT_P(cstring_to_text(result_message));
+}
+
+void
+_PG_init()
+{
+    srand(time(NULL));
+}
\ No newline at end of file
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
new file mode 100644
index 0000000000..5c51a5ef81
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements--1.0.sql
@@ -0,0 +1,7 @@
+create function bench_oid_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_oid_sort' LANGUAGE C;
+
+create function bench_int_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int_sort' LANGUAGE C;
+
+create function bench_int16_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_int16_sort' LANGUAGE C;
+
+create function bench_float8_sort(integer) returns text AS 'MODULE_PATHNAME', 'bench_float8_sort' LANGUAGE C;
diff --git a/contrib/bench_sort_improvements/bench_sort_improvements.control b/contrib/bench_sort_improvements/bench_sort_improvements.control
new file mode 100644
index 0000000000..7dc05056ac
--- /dev/null
+++ b/contrib/bench_sort_improvements/bench_sort_improvements.control
@@ -0,0 +1,5 @@
+# fuzzystrmatch extension
+comment = 'test extension'
+default_version = '1.0'
+module_pathname = '$libdir/bench_sort_improvements'
+relocatable = true
-- 
2.43.0

v2-0008-Replace-qsort-calls-with-the-new-sorting-template.patchtext/x-patch; charset=US-ASCII; name=v2-0008-Replace-qsort-calls-with-the-new-sorting-template.patchDownload
From b55e9c3ea08140928589d25275e00cc68f0edbe9 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:13 +0700
Subject: [PATCH v2 08/10] Replace qsort calls with the new sorting template
 `sort_float8_arr` for better performance in spg_box_quad_picksplit function.

---
 src/backend/utils/adt/geo_spgist.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index fa0a753fa4..e9ce26d816 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -472,10 +472,10 @@ spg_box_quad_picksplit(PG_FUNCTION_ARGS)
 		highYs[i] = box->high.y;
 	}
 
-	qsort(lowXs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(highXs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(lowYs, in->nTuples, sizeof(float8), compareDoubles);
-	qsort(highYs, in->nTuples, sizeof(float8), compareDoubles);
+	sort_float8_arr(lowXs, in->nTuples);
+	sort_float8_arr(highXs, in->nTuples);
+	sort_float8_arr(lowYs, in->nTuples);
+	sort_float8_arr(highYs, in->nTuples);
 
 	median = in->nTuples / 2;
 
-- 
2.43.0

v2-0010-Refactor-LSN-Sorting-to-Use-Template-Based-sort_c.patchtext/x-patch; charset=US-ASCII; name=v2-0010-Refactor-LSN-Sorting-to-Use-Template-Based-sort_c.patchDownload
From fafe3079d4a1316f88e3b5107a7131fe0b028c58 Mon Sep 17 00:00:00 2001
From: Stepan Neretin <sndcppg@gmail.com>
Date: Sun, 8 Sep 2024 15:44:22 +0700
Subject: [PATCH v2 10/10] Refactor LSN Sorting to Use Template-Based
 sort_cmp_lsn

Refactored the sorting logic for Write, Flush, and Apply LSN arrays in SyncRepGetNthLatestSyncRecPtr to replace qsort with the optimized sort_cmp_lsn function, which leverages a template-based sorting mechanism.
---
 src/backend/replication/syncrep.c | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index fa5988c824..bea4b7275b 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -118,7 +118,6 @@ static void SyncRepGetNthLatestSyncRecPtr(XLogRecPtr *writePtr,
 										  uint8 nth);
 static int	SyncRepGetStandbyPriority(void);
 static int	standby_priority_comparator(const void *a, const void *b);
-static int	cmp_lsn(const void *a, const void *b);
 
 #ifdef USE_ASSERT_CHECKING
 static bool SyncRepQueueIsOrderedByLSN(int mode);
@@ -642,6 +641,16 @@ SyncRepGetOldestSyncRecPtr(XLogRecPtr *writePtr,
 	}
 }
 
+/*
+- * Compare lsn in order to sort array in descending order.
+-*/
+#define ST_SORT sort_cmp_lsn
+#define ST_ELEMENT_TYPE XLogRecPtr
+#define ST_COMPARE(a, b) pg_cmp_u64(*(b), *(a))  /* Dereference pointers for comparison */
+#define ST_SCOPE static
+#define ST_DEFINE
+#include <lib/sort_template.h>
+
 /*
  * Calculate the Nth latest Write, Flush and Apply positions among sync
  * standbys.
@@ -674,9 +683,9 @@ SyncRepGetNthLatestSyncRecPtr(XLogRecPtr *writePtr,
 	}
 
 	/* Sort each array in descending order */
-	qsort(write_array, num_standbys, sizeof(XLogRecPtr), cmp_lsn);
-	qsort(flush_array, num_standbys, sizeof(XLogRecPtr), cmp_lsn);
-	qsort(apply_array, num_standbys, sizeof(XLogRecPtr), cmp_lsn);
+	sort_cmp_lsn(write_array, num_standbys);
+	sort_cmp_lsn(flush_array, num_standbys);
+	sort_cmp_lsn(apply_array, num_standbys);
 
 	/* Get Nth latest Write, Flush, Apply positions */
 	*writePtr = write_array[nth - 1];
@@ -688,18 +697,6 @@ SyncRepGetNthLatestSyncRecPtr(XLogRecPtr *writePtr,
 	pfree(apply_array);
 }
 
-/*
- * Compare lsn in order to sort array in descending order.
- */
-static int
-cmp_lsn(const void *a, const void *b)
-{
-	XLogRecPtr	lsn1 = *((const XLogRecPtr *) a);
-	XLogRecPtr	lsn2 = *((const XLogRecPtr *) b);
-
-	return pg_cmp_u64(lsn2, lsn1);
-}
-
 /*
  * Return data about walsenders that are candidates to be sync standbys.
  *
-- 
2.43.0

#12David Rowley
dgrowleyml@gmail.com
In reply to: Stepan Neretin (#11)
Re: Sort functions with specialized comparators

On Sun, 8 Sept 2024 at 20:51, Stepan Neretin <sndcppg@gmail.com> wrote:

Hi! I rebase, clean and some refactor my patches.

I'm unsure what exactly is going on with this thread. It started with
Andrey proposing a patch to speed up intarray sorting and now it's
turned into you proposing 10 patches which implement a series of sort
specialisation functions without any justification as to why the
change is useful.

If you want to have a performance patch accepted, then you'll need to
show your test case and the performance results before and after.

What this patch series looks like to me is that you've just searched
the code base for qsort and just implemented a specialised qsort
version without any regard as to whether the change is useful or not.
For example, looking at v2-0006, you've added a specialisation to sort
the columns which are specified in the CREATE STATISTICS command. This
seems highly unlikely to be useful. The number of elements in this
array is limited by STATS_MAX_DIMENSIONS, which is 8. Are you claiming
the sort specialisation you've added makes a meaningful performance
improvement to sorting an 8 element array?

It looks to me like you've just derailed Andrey's proposal. I suggest
you validate which ones of these patches you can demonstrate produce a
meaningful performance improvement, ditch the remainder, and then
start your own thread showing your test case and results.

David

#13Stepan Neretin
sndcppg@gmail.com
In reply to: David Rowley (#12)
Re: Sort functions with specialized comparators

Hi, why do you think that I rejected Andrey's offer? I included his patch
first in my own. Yes, patch 2-0006 is the only patch to which I have not
attached any statistics and it looks really dubious. But the rest seem
useful. Above, I attached a speed graph for one of the patches and tps(
pgbench)
What do you think is the format in which to make benchmarks for my patches?
Best regards, Stepan Neretin.

#14David Rowley
dgrowleyml@gmail.com
In reply to: Stepan Neretin (#13)
Re: Sort functions with specialized comparators

On Mon, 9 Sept 2024 at 01:00, Stepan Neretin <sndcppg@gmail.com> wrote:

Hi, why do you think that I rejected Andrey's offer? I included his patch first in my own. Yes, patch 2-0006 is the only patch to which I have not attached any statistics and it looks really dubious. But the rest seem useful. Above, I attached a speed graph for one of the patches and tps(pgbench)

The difference with your patches and Andrey's patch is that he
included a benchmark which is targeted to the code he changed and his
results show a speed-up.

What it appears that you've done is made an assortment of changes and
picked the least effort thing that tests performance of something. You
by chance saw a performance increase so assumed it was due to your
changes.

What do you think is the format in which to make benchmarks for my patches?

You'll need a benchmark that exercises the code you've changed to some
degree where it has a positive impact on performance. As far as I can
see, you've not done that yet.

Just to give you the benefit of the doubt, I applied all 10 v2 patches
and adjusted the call sites to add a NOTICE to include the size of the
array being sorted. Here is the result of running your benchmark:

$ pgbench -t1000 -d postgres
pgbench (18devel)
NOTICE: RelationGetIndexList 1
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 3
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 2
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 1
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 2
NOTICE: RelationGetStatExtList 0
starting vacuum...NOTICE: RelationGetIndexList 1
NOTICE: RelationGetIndexList 0
end.
NOTICE: RelationGetIndexList 1
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 1
NOTICE: RelationGetStatExtList 0
NOTICE: RelationGetIndexList 1
NOTICE: RelationGetStatExtList 0
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
maximum number of tries: 1
number of transactions per client: 1000
number of transactions actually processed: 1000/1000
number of failed transactions: 0 (0.000%)
latency average = 0.915 ms
initial connection time = 23.443 ms
tps = 1092.326732 (without initial connection time)

Note that -t1000 shows the same number of notices as -t1.

So, it seems everything you've changed that runs in your benchmark is
RelationGetIndexList() and RelationGetStatExtList(). In one of the
calls to RelationGetIndexList() we're sorting up to a maximum of 3
elements.

Just to be clear, I'm not stating that I think all of your changes are
useless. If you want these patches accepted, then you're going to need
to prove they're useful and you've not done that.

Also, unless Andrey is happy for you to tag onto the work he's doing,
I'd suggest another thread for that work. I don't think there's any
good reason for that work to delay Andrey's work.

David

#15Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: David Rowley (#14)
Re: Sort functions with specialized comparators

On 9 Sep 2024, at 02:31, David Rowley <dgrowleyml@gmail.com> wrote:

Also, unless Andrey is happy for you to tag onto the work he's doing,
I'd suggest another thread for that work. I don't think there's any
good reason for that work to delay Andrey's work.

Stepan asked for mentoring project, so I handed him this patch set. We are working together, but the main goal is integrating Stepan into dev process. Well, the summer was really hot and we somehow were not advancing the project… So your thread bump is very timely!
Many thanks for your input about benchmarks! We will focus on measuring impact of changes. I totally share your concerns about optimization of sorts that are not used frequently.

Best regards, Andrey Borodin.

#16Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: Andrey M. Borodin (#1)
1 attachment(s)
Re: Sort functions with specialized comparators

On 2 Dec 2024, at 08:39, John Naylor <johncnaylorls@gmail.com> wrote:

On Mon, Dec 2, 2024 at 1:12 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 25 Nov 2024, at 17:50, John Naylor <johncnaylorls@gmail.com> wrote:

I'd like to see the two sort specializations combined
into one, using a local comparator that knows when to reverse the
comparison result (hope that makes sense).

Sure, please find attached.
The prototype looks somewhat ugly (we pass bool* ascending instead of bool ascending) and it cost us ~2% of performance (on my MacBook Air M3).

I haven't tried to reproduce, but the comparison function has a
different style for DESC than the tuplesort comparators, and the style
here has worse code density, at least in isolation. You can see the
difference in the link below. I also found a way to make the cmp
reversal branch-free (last example). That may not survive once it's
inlined, of course, or could make things worse, but you can try these
if you like:

https://godbolt.org/z/nfPMT7Enr

On my machine this test
\timing on
SELECT (sort(arr))[1] FROM arrays_to_sort;\watch 0 c=5

produces
sort_int32_cmp Time: 543.690 ms
sort_int32_cmp_2 Time: 609.019 ms
sort_int32_cmp_4 Time: 612.219 ms

So, I'd stick with sort_int32_cmp. But, perhaps, on Intel we might have different results.

But is not more generic.

A lot of the churn is from v1, and made worse by v2, and that seems to
be from getting rid of the QSORT macro:

@@ -227,9 +228,10 @@ Datum
sort_asc(PG_FUNCTION_ARGS)
{
ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ bool ascending = true;

CHECKARRVALID(a);
- QSORT(a, 1);
+ sort_int32(ARRPTR(a), ARRNELEMS(a), &ascending);
PG_RETURN_POINTER(a);
}

The macro hides some details -- can we put "ascending" inside there?

Done.

Best regards, Andrey Borodin.

Attachments:

v3-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v3-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From be4e7c85cad0e6f9d7289f38c20cc581fc50eb62 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v3] Use specialized sort facilities

---
 contrib/intarray/_int.h      |  8 +---
 contrib/intarray/_int_tool.c | 14 +------
 src/include/port.h           |  1 +
 src/port/qsort.c             | 73 ++++++++++++++++++++++++++++++++++++
 4 files changed, 77 insertions(+), 19 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..fb965cb035 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -42,7 +42,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -176,16 +176,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..c49097bc7d 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -392,16 +392,4 @@ int_to_intset(int32 elem)
 	aa = ARRPTR(result);
 	aa[0] = elem;
 	return result;
-}
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
+}
\ No newline at end of file
diff --git a/src/include/port.h b/src/include/port.h
index ba9ab0d34f..72dc42daa6 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -443,6 +443,7 @@ extern char *strsep(char **stringp, const char *delim);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
diff --git a/src/port/qsort.c b/src/port/qsort.c
index 7879e6cd56..a3c43c0dd0 100644
--- a/src/port/qsort.c
+++ b/src/port/qsort.c
@@ -20,3 +20,76 @@ pg_qsort_strcmp(const void *a, const void *b)
 {
 	return strcmp(*(const char *const *) a, *(const char *const *) b);
 }
+
+static inline int
+sort_int32_cmp(int32* a, int32* b, bool* ascending)
+{
+	if (*ascending)
+	{
+		if (*a < *b)
+			return -1;
+		if (*a > *b)
+			return 1;
+	}
+	else
+	{
+		if (*a < *b)
+			return 1;
+		if (*a > *b)
+			return -1;
+	}
+	return 0;
+}
+
+static inline int
+sort_int32_cmp_2(int* a, int* b, bool* ascending)
+{
+	int result;
+
+	if (*a < *b)
+		result = -1;
+	else if (*a > *b)
+		result = 1;
+	else
+		result = 0;
+
+	if (!*ascending)
+		result = -result;
+
+	return result;
+}
+
+static inline int
+sort_int32_cmp_3(int* a, int* b, bool* ascending)
+{
+	int result;
+	bool asc = *ascending;
+
+	if (*a < *b)
+		result = -1;
+	else if (*a > *b)
+		result = 1;
+	else
+		result = 0;
+
+	if (!asc)
+		result = -result;
+
+	return result;
+}
+
+#define ST_SORT sort_int32_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) sort_int32_cmp(a, b, ascending)
+//#define ST_COMPARE(a, b, ascending) sort_int32_cmp_2(a, b, ascending)
+//#define ST_COMPARE(a, b, ascending) sort_int32_cmp_3(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE bool
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	sort_int32_impl(base, nel, &ascending);
+}
-- 
2.39.5 (Apple Git-154)

#17John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#16)
Re: Sort functions with specialized comparators

On Wed, Dec 4, 2024 at 2:47 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

sort_int32_cmp Time: 543.690 ms
sort_int32_cmp_2 Time: 609.019 ms
sort_int32_cmp_4 Time: 612.219 ms

So, I'd stick with sort_int32_cmp. But, perhaps, on Intel we might have different results.

I tried on an older Intel chip and got similar results, so we'll go
with your original comparator:

master: latency average = 1867.878 ms
cmp1: latency average = 1189.225 ms
cmp2: latency average = 1341.153 ms
cmp3: latency average = 1270.053 ms

I believe src/port/qsort.c was meant to be just for the standard sort
interface as found in a C library. We do have one globally visible
special sort here:
src/backend/utils/sort/qsort_interruptible.c
...so that directory seems a better fit. The declaration is in
src/include/port.h, though. Note: that one doesn't have a global
wrapper around a static function -- it's declared global since
ST_SCOPE is not defined.

And one more bikeshedding bit that might get noticed: tuplesorts
express their boolean as "reversed". We don't necessarily need to
follow that, but consistency is good for readability.

--
John Naylor
Amazon Web Services

#18Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#17)
1 attachment(s)
Re: Sort functions with specialized comparators

On 5 Dec 2024, at 15:16, John Naylor <johncnaylorls@gmail.com> wrote:

I tried on an older Intel chip and got similar results, so we'll go
with your original comparator:

Ack.

I believe src/port/qsort.c was meant to be just for the standard sort
interface as found in a C library. We do have one globally visible
special sort here:
src/backend/utils/sort/qsort_interruptible.c
...so that directory seems a better fit.

OK. BTW do we need ST_CHECK_FOR_INTERRUPTS?

The declaration is in
src/include/port.h, though. Note: that one doesn't have a global
wrapper around a static function -- it's declared global since
ST_SCOPE is not defined.

Added static.

And one more bikeshedding bit that might get noticed: tuplesorts
express their boolean as "reversed". We don't necessarily need to
follow that, but consistency is good for readability.

I do not know if "reversed sorting order" is more idiomatic than "ascending sorting order". If you think it is - let's switch argument's name to "reversed".

Best regards, Andrey Borodin.

Attachments:

v4-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v4-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From efd1cb010809678e080a647b1581dc5cc61da959 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v4] Use specialized sort facilities

---
 contrib/intarray/_int.h                      |  8 ++---
 contrib/intarray/_int_tool.c                 | 12 -------
 src/backend/utils/sort/qsort_interruptible.c | 34 ++++++++++++++++++++
 src/include/port.h                           |  1 +
 4 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..fb965cb035 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -42,7 +42,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -176,16 +176,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..e83c6aadc6 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -393,15 +393,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
diff --git a/src/backend/utils/sort/qsort_interruptible.c b/src/backend/utils/sort/qsort_interruptible.c
index f179b25624..b2d984acc2 100644
--- a/src/backend/utils/sort/qsort_interruptible.c
+++ b/src/backend/utils/sort/qsort_interruptible.c
@@ -14,3 +14,37 @@
 #define ST_DEFINE
 #define ST_CHECK_FOR_INTERRUPTS
 #include "lib/sort_template.h"
+
+static inline int
+sort_int32_cmp(int32* a, int32* b, bool* ascending)
+{
+	if (*ascending)
+	{
+		if (*a < *b)
+			return -1;
+		if (*a > *b)
+			return 1;
+	}
+	else
+	{
+		if (*a < *b)
+			return 1;
+		if (*a > *b)
+			return -1;
+	}
+	return 0;
+}
+
+#define ST_SORT sort_int32_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) sort_int32_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE bool
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	sort_int32_impl(base, nel, &ascending);
+}
diff --git a/src/include/port.h b/src/include/port.h
index ba9ab0d34f..72dc42daa6 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -443,6 +443,7 @@ extern char *strsep(char **stringp, const char *delim);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
-- 
2.39.5 (Apple Git-154)

#19John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#18)
Re: Sort functions with specialized comparators

On Fri, Dec 6, 2024 at 1:32 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 5 Dec 2024, at 15:16, John Naylor <johncnaylorls@gmail.com> wrote:

I believe src/port/qsort.c was meant to be just for the standard sort
interface as found in a C library. We do have one globally visible
special sort here:
src/backend/utils/sort/qsort_interruptible.c
...so that directory seems a better fit.

OK. BTW do we need ST_CHECK_FOR_INTERRUPTS?

That's a good thing to raise right now -- intarray currently doesn't
have one, and we haven't gotten complaints from people trying to sort
large arrays and cancel the query. This extension is not commonly
used, so that's not surprising. It could be that large arrays are even
less common, or no one bothered to report it. What's the largest size
that your customers use?

If we do need a check for interrupts, then this whole thing must
remain private to intarray. From reading e64cdab0030 , it's not safe
to interrupt in general.

And one more bikeshedding bit that might get noticed: tuplesorts
express their boolean as "reversed". We don't necessarily need to
follow that, but consistency is good for readability.

I do not know if "reversed sorting order" is more idiomatic than "ascending sorting order". If you think it is - let's switch argument's name to "reversed".

After sleeping on it, I actually think it's mildly ridiculous for this
module to force the comparator to know about the sort direction.
Tuplesorts must do that because each sort key could have a different
sort order. There is only one place in intarray that wants reversed
order -- maybe that place should reverse elements itself? It's fine to
keep thing as they are if the sort function stays private to intarray,
but this patch creates a global function, where the "ascending"
parameter is just noise. And if we don't have large int32 sorts
outside of intarray, then the path of least resistance may be to keep
it private.

I had a look at the files touched by this patch and noticed that there
is another sort used for making arrays unique. Were you going to look
at that as well? That reminded me of a patchset from Thomas Munro that
added bsearch and unique macros to the sort template -- see 0001-0003
in the link below. (That also includes a proposal to have a
specialization for uint32 -- I'm still not sure if that would have a
performance benefit for real workloads, but I believe the motivation
was mostly cosmetic):

/messages/by-id/CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com

--
John Naylor
Amazon Web Services

#20Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#19)
1 attachment(s)
Re: Sort functions with specialized comparators

On 6 Dec 2024, at 08:49, John Naylor <johncnaylorls@gmail.com> wrote:

/messages/by-id/CA+hUKGKztHEWm676csTFjYzortziWmOcf8HDss2Zr0muZ2xfEg@mail.gmail.com

Wow, what a thread!
"Simpsons Already Did It"

On Fri, Dec 6, 2024 at 1:32 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 5 Dec 2024, at 15:16, John Naylor <johncnaylorls@gmail.com> wrote:

I believe src/port/qsort.c was meant to be just for the standard sort
interface as found in a C library. We do have one globally visible
special sort here:
src/backend/utils/sort/qsort_interruptible.c
...so that directory seems a better fit.

OK. BTW do we need ST_CHECK_FOR_INTERRUPTS?

That's a good thing to raise right now -- intarray currently doesn't
have one, and we haven't gotten complaints from people trying to sort
large arrays and cancel the query. This extension is not commonly
used, so that's not surprising. It could be that large arrays are even
less common, or no one bothered to report it. What's the largest size
that your customers use?

If we do need a check for interrupts, then this whole thing must
remain private to intarray. From reading e64cdab0030 , it's not safe
to interrupt in general.

I think commit message states that it's better to opt-in for interruptible sort. So I do not think making sort interruptible is a blocker for making global specialized sorting routines.

And one more bikeshedding bit that might get noticed: tuplesorts
express their boolean as "reversed". We don't necessarily need to
follow that, but consistency is good for readability.

I do not know if "reversed sorting order" is more idiomatic than "ascending sorting order". If you think it is - let's switch argument's name to "reversed".

After sleeping on it, I actually think it's mildly ridiculous for this
module to force the comparator to know about the sort direction.
Tuplesorts must do that because each sort key could have a different
sort order. There is only one place in intarray that wants reversed
order -- maybe that place should reverse elements itself? It's fine to
keep thing as they are if the sort function stays private to intarray,
but this patch creates a global function, where the "ascending"
parameter is just noise. And if we don't have large int32 sorts
outside of intarray, then the path of least resistance may be to keep
it private.

We could use global function for oid lists which may be arbitrary large.
But if you think that local intarray function is better - let's go that route.

I had a look at the files touched by this patch and noticed that there
is another sort used for making arrays unique. Were you going to look
at that as well?

Done.

Best regards, Andrey Borodin.

Attachments:

v5-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v5-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From ff09aab5ab7c2f89cb9b5b3237f2294273e2de16 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v5] Use specialized sort facilities

---
 contrib/intarray/_int.h                      | 15 ++---
 contrib/intarray/_int_tool.c                 | 59 ++++----------------
 src/backend/utils/sort/qsort_interruptible.c | 35 ++++++++++++
 src/include/port.h                           |  1 +
 4 files changed, 54 insertions(+), 56 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..3311a415bf 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -42,7 +42,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -50,8 +50,10 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		{ \
+			sort_int32(ARRPTR(x), _nelems_, true); \
+			(x) = _int_unique(x); \
+		} \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +111,6 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +177,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..06f78547a9 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -186,37 +186,6 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
-isort_cmp(const void *a, const void *b, void *arg)
-{
-	int32		aval = *((const int32 *) a);
-	int32		bval = *((const int32 *) b);
-
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
-	return 0;
-}
-
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
-
 /* Create a new int array with room for "num" elements */
 ArrayType *
 new_intArrayType(int num)
@@ -306,15 +275,23 @@ internal_size(int *a, int len)
 	return (int) size;
 }
 
+static int
+unique_cmp(const void *a, const void *b)
+{
+	int32 aval = *((const int32 *) a);
+	int32 bval = *((const int32 *) b);
+
+	return pg_cmp_s32(aval, bval);
+}
+
+
 /* unique-ify elements of r in-place ... r must be sorted already */
 ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
 
-	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+	num = qunique(ARRPTR(r), num, sizeof(int), unique_cmp);
 
 	return resize_intArrayType(r, num);
 }
@@ -392,16 +369,4 @@ int_to_intset(int32 elem)
 	aa = ARRPTR(result);
 	aa[0] = elem;
 	return result;
-}
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
+}
\ No newline at end of file
diff --git a/src/backend/utils/sort/qsort_interruptible.c b/src/backend/utils/sort/qsort_interruptible.c
index f179b25624..b893c6ba0f 100644
--- a/src/backend/utils/sort/qsort_interruptible.c
+++ b/src/backend/utils/sort/qsort_interruptible.c
@@ -14,3 +14,38 @@
 #define ST_DEFINE
 #define ST_CHECK_FOR_INTERRUPTS
 #include "lib/sort_template.h"
+
+static inline int
+sort_int32_cmp(int32* a, int32* b, bool* ascending)
+{
+	if (*ascending)
+	{
+		if (*a < *b)
+			return -1;
+		if (*a > *b)
+			return 1;
+	}
+	else
+	{
+		if (*a < *b)
+			return 1;
+		if (*a > *b)
+			return -1;
+	}
+	return 0;
+}
+
+#define ST_SORT sort_int32_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) sort_int32_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE bool
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#define ST_CHECK_FOR_INTERRUPTS
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	sort_int32_impl(base, nel, &ascending);
+}
diff --git a/src/include/port.h b/src/include/port.h
index ba9ab0d34f..72dc42daa6 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -443,6 +443,7 @@ extern char *strsep(char **stringp, const char *delim);
 extern void pg_qsort(void *base, size_t nel, size_t elsize,
 					 int (*cmp) (const void *, const void *));
 extern int	pg_qsort_strcmp(const void *a, const void *b);
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
 
 #define qsort(a,b,c,d) pg_qsort(a,b,c,d)
 
-- 
2.39.5 (Apple Git-154)

#21John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#20)
Re: Sort functions with specialized comparators

On Mon, Dec 9, 2024 at 8:02 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 6 Dec 2024, at 08:49, John Naylor <johncnaylorls@gmail.com> wrote:

That's a good thing to raise right now -- intarray currently doesn't
have one, and we haven't gotten complaints from people trying to sort
large arrays and cancel the query. This extension is not commonly
used, so that's not surprising. It could be that large arrays are even
less common, or no one bothered to report it. What's the largest size
that your customers use?

If we do need a check for interrupts, then this whole thing must
remain private to intarray. From reading e64cdab0030 , it's not safe
to interrupt in general.

I think commit message states that it's better to opt-in for interruptible sort. So I do not think making sort interruptible is a blocker for making global specialized sorting routines.

There is a difference, though -- that commit had a number of uses for
it immediately. In my view, there is no reason to have a global
interruptible sort that's only used by one contrib module. YAGNI

Also, I was hoping get an answer for how this would actually affect
intarray use you've seen in the wild. If the answer is "I don't know
of any one who uses this either", then I'm actually starting to wonder
if the speed matters at all. Maybe all uses are for a few hundred or
thousand integers, in which case the sort time is trivial anyway?

We could use global function for oid lists which may be arbitrary large.

BTW, oids are unsigned. (See the 0002 patch from Thomas M. I linked to earlier)

--
John Naylor
Amazon Web Services

#22Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#21)
Re: Sort functions with specialized comparators

On 11 Dec 2024, at 11:39, John Naylor <johncnaylorls@gmail.com> wrote:

On Mon, Dec 9, 2024 at 8:02 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

I think commit message states that it's better to opt-in for interruptible sort. So I do not think making sort interruptible is a blocker for making global specialized sorting routines.

There is a difference, though -- that commit had a number of uses for
it immediately. In my view, there is no reason to have a global
interruptible sort that's only used by one contrib module. YAGNI

Also, I was hoping get an answer for how this would actually affect
intarray use you've seen in the wild. If the answer is "I don't know
of any one who uses this either", then I'm actually starting to wonder
if the speed matters at all. Maybe all uses are for a few hundred or
thousand integers, in which case the sort time is trivial anyway?

I do not have access to user data in most clusters... I remember only one particular case: tags and folders applied to mail messages are represented by int array. Mostly for GIN search. In that case vast majority of these arrays are 0-10 elements, some hot-acceses fraction of 10-1000. Only robots (service accounts) can have millions, and in their case latency have no impact at all.
But this particular case also does not trigger sorting much: arrays are stored sorted and modifications are infrequent. In most cases sorting is invoked for already sorted or almost sorted input.

So yeah, from practical point of view cosmetic reasons seems to be most important :)

We could use global function for oid lists which may be arbitrary large.

BTW, oids are unsigned. (See the 0002 patch from Thomas M. I linked to earlier)

Seems like we cannot reuse same function...

So, let's do the function private for intarray and try to remove as much code as possible?

Best regards, Andrey Borodin.

#23John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#22)
Re: Sort functions with specialized comparators

On Mon, Dec 16, 2024 at 12:58 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 11 Dec 2024, at 11:39, John Naylor <johncnaylorls@gmail.com> wrote:

Also, I was hoping get an answer for how this would actually affect
intarray use you've seen in the wild. If the answer is "I don't know
of any one who uses this either", then I'm actually starting to wonder
if the speed matters at all. Maybe all uses are for a few hundred or
thousand integers, in which case the sort time is trivial anyway?

I do not have access to user data in most clusters... I remember only one particular case: tags and folders applied to mail messages are represented by int array. Mostly for GIN search. In that case vast majority of these arrays are 0-10 elements, some hot-acceses fraction of 10-1000. Only robots (service accounts) can have millions, and in their case latency have no impact at all.
But this particular case also does not trigger sorting much: arrays are stored sorted and modifications are infrequent. In most cases sorting is invoked for already sorted or almost sorted input.

Okay, if one case uses millions, than surely others also do so.

So yeah, from practical point of view cosmetic reasons seems to be most important :)

Seems worth doing.

--
John Naylor
Amazon Web Services

#24John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#22)
Re: Sort functions with specialized comparators

On Mon, Dec 16, 2024 at 12:58 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

So, let's do the function private for intarray and try to remove as much code as possible?

Sorry, I forgot this part earlier. Yes, let's have the private function.

--
John Naylor
Amazon Web Services

#25Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#24)
3 attachment(s)
Re: Sort functions with specialized comparators

On 16 Dec 2024, at 14:02, John Naylor <johncnaylorls@gmail.com> wrote:

Sorry, I forgot this part earlier. Yes, let's have the private function.

PFA v6.

I was poking around intarray and trying not to bash everything. It seems to me that overall code readability should be seriously reworked. Even if no one is going to invent anything new around this code. Looks like overall project code standards matured far above, but this particular extension is forgotten. There is a lot of useless checks and optimizations for n < 2, copying data to new allocation when it's not necessary, small inconsistencies etc.

I don't think it's a matter of this particular thread though.

Best regards, Andrey Borodin.

Attachments:

v6-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v6-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From 5065fc3d717d7c053553f2a54dba020791a07fba Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v6 1/4] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 17 ++++---
 contrib/intarray/_int_tool.c | 91 ++++++++++++++++++------------------
 2 files changed, 53 insertions(+), 55 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..1da2f36f99 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -22,6 +22,8 @@ typedef struct
 	int			num_ranges;		/* number of ranges */
 } GISTIntArrayOptions;
 
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
+
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
 #define ARRNELEMS(x)  ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x))
@@ -42,7 +44,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -50,8 +52,10 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		{ \
+			sort_int32(ARRPTR(x), _nelems_, true); \
+			(x) = _int_unique(x); \
+		} \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +113,6 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +179,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..049c0fda4a 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -186,37 +186,6 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
-isort_cmp(const void *a, const void *b, void *arg)
-{
-	int32		aval = *((const int32 *) a);
-	int32		bval = *((const int32 *) b);
-
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
-	return 0;
-}
-
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
-
 /* Create a new int array with room for "num" elements */
 ArrayType *
 new_intArrayType(int num)
@@ -306,15 +275,57 @@ internal_size(int *a, int len)
 	return (int) size;
 }
 
+static inline int
+sort_int32_cmp(int32* a, int32* b, bool* ascending)
+{
+	if (*ascending)
+	{
+		if (*a < *b)
+			return -1;
+		if (*a > *b)
+			return 1;
+	}
+	else
+	{
+		if (*a < *b)
+			return 1;
+		if (*a > *b)
+			return -1;
+	}
+	return 0;
+}
+
+#define ST_SORT sort_int32_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) sort_int32_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE bool
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	sort_int32_impl(base, nel, &ascending);
+}
+
+static int
+unique_cmp(const void *a, const void *b)
+{
+	int32 aval = *((const int32 *) a);
+	int32 bval = *((const int32 *) b);
+
+	return pg_cmp_s32(aval, bval);
+}
+
+
 /* unique-ify elements of r in-place ... r must be sorted already */
 ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
 
-	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+	num = qunique(ARRPTR(r), num, sizeof(int), unique_cmp);
 
 	return resize_intArrayType(r, num);
 }
@@ -393,15 +404,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
-- 
2.39.5 (Apple Git-154)

v6-0003-prefer-non-resizing-to-constructing-empty-array.patchapplication/octet-stream; name=v6-0003-prefer-non-resizing-to-constructing-empty-array.patch; x-unix-mode=0644Download
From 2cf5a7047e86ce5cc36ecb2ee9d4739cf4a9aac9 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 20 Dec 2024 00:19:58 +0500
Subject: [PATCH v6 3/4] prefer non-resizing to constructing empty array

---
 contrib/intarray/_int_tool.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index 049c0fda4a..031812ec99 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -221,6 +221,9 @@ resize_intArrayType(ArrayType *a, int num)
 	int			nbytes;
 	int			i;
 
+	if (num == ARRNELEMS(a))
+		return a;
+
 	/* if no elements, return a zero-dimensional array */
 	if (num <= 0)
 	{
@@ -229,9 +232,6 @@ resize_intArrayType(ArrayType *a, int num)
 		return a;
 	}
 
-	if (num == ARRNELEMS(a))
-		return a;
-
 	nbytes = ARR_DATA_OFFSET(a) + sizeof(int) * num;
 
 	a = (ArrayType *) repalloc(a, nbytes);
-- 
2.39.5 (Apple Git-154)

v6-0002-Use-PREPAREARR-where-possible.patchapplication/octet-stream; name=v6-0002-Use-PREPAREARR-where-possible.patch; x-unix-mode=0644Download
From 4cf1553d9fda452188fb991ad3e6120e7cb31e2a Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 20 Dec 2024 00:19:22 +0500
Subject: [PATCH v6 2/4] Use PREPAREARR where possible

---
 contrib/intarray/_int_gist.c |  3 +--
 contrib/intarray/_int_op.c   | 10 ++++------
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index a09b7fa812..ed66abe5a8 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -150,8 +150,7 @@ g_int_union(PG_FUNCTION_ARGS)
 		ptr += nel;
 	}
 
-	QSORT(res, 1);
-	res = _int_unique(res);
+	PREPAREARR(res);
 	*size = VARSIZE(res);
 	PG_RETURN_POINTER(res);
 }
diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c
index 5b164f6788..0025cd3937 100644
--- a/contrib/intarray/_int_op.c
+++ b/contrib/intarray/_int_op.c
@@ -381,8 +381,8 @@ intset_union_elem(PG_FUNCTION_ARGS)
 
 	result = intarray_add_elem(a, PG_GETARG_INT32(1));
 	PG_FREE_IF_COPY(a, 0);
-	QSORT(result, 1);
-	PG_RETURN_POINTER(_int_unique(result));
+	PREPAREARR(result);
+	PG_RETURN_POINTER(result);
 }
 
 Datum
@@ -403,11 +403,9 @@ intset_subtract(PG_FUNCTION_ARGS)
 	CHECKARRVALID(a);
 	CHECKARRVALID(b);
 
-	QSORT(a, 1);
-	a = _int_unique(a);
+	PREPAREARR(a);
 	ca = ARRNELEMS(a);
-	QSORT(b, 1);
-	b = _int_unique(b);
+	PREPAREARR(b);
 	cb = ARRNELEMS(b);
 	result = new_intArrayType(ca);
 	aa = ARRPTR(a);
-- 
2.39.5 (Apple Git-154)

#26John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#25)
Re: Sort functions with specialized comparators

On Sat, Dec 21, 2024 at 12:16 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 16 Dec 2024, at 14:02, John Naylor <johncnaylorls@gmail.com> wrote:

Sorry, I forgot this part earlier. Yes, let's have the private function.

PFA v6.

v6-0001:

+static int
+unique_cmp(const void *a, const void *b)
+{
+ int32 aval = *((const int32 *) a);
+ int32 bval = *((const int32 *) b);
+
+ return pg_cmp_s32(aval, bval);
+}

I'm not sure it makes sense to create a whole new function for this,
when the same patch removed:

-int
-compASC(const void *a, const void *b)
-{
- return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}

...which in effect the exact same thing.

Otherwise seems close to committable.

v6-0002: Seems like a good idea to be more consistent, but I admit I'm
not much a fan of little indirection macros like this. It makes the
code less readable in my view.

v6-0003: I didn't feel like digging further. It's interesting that
inner_int_inter() takes care to detect the zero-length case and free
the old array to avoid resizing.

--
John Naylor
Amazon Web Services

#27Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#26)
2 attachment(s)
Re: Sort functions with specialized comparators

On 4 Jan 2025, at 10:24, John Naylor <johncnaylorls@gmail.com> wrote:

v6-0001:

+static int
+unique_cmp(const void *a, const void *b)
+{
+ int32 aval = *((const int32 *) a);
+ int32 bval = *((const int32 *) b);
+
+ return pg_cmp_s32(aval, bval);
+}

I'm not sure it makes sense to create a whole new function for this,
when the same patch removed:

-int
-compASC(const void *a, const void *b)
-{
- return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}

...which in effect the exact same thing.

Otherwise seems close to committable.

I thought about it, but decided to rename the routine.
Here's a version 7 with compASC().
And, just in case, if we already have ASC, why not keep DESC too instead of newly invented cmp function :) PFA v8.

Thanks!

Best regards, Andrey Borodin.

Attachments:

v7-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v7-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From 68db077a6970c15d7b13dcb239f6249d72084513 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v7] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 17 ++++----
 contrib/intarray/_int_tool.c | 79 +++++++++++++++++-------------------
 2 files changed, 46 insertions(+), 50 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..1da2f36f99 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -22,6 +22,8 @@ typedef struct
 	int			num_ranges;		/* number of ranges */
 } GISTIntArrayOptions;
 
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
+
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
 #define ARRNELEMS(x)  ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x))
@@ -42,7 +44,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -50,8 +52,10 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		{ \
+			sort_int32(ARRPTR(x), _nelems_, true); \
+			(x) = _int_unique(x); \
+		} \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +113,6 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +179,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..ea99b2c4de 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -10,6 +10,8 @@
 #include "common/int.h"
 #include "lib/qunique.h"
 
+static inline int compASC(const void *a, const void *b);
+
 /* arguments are assumed sorted & unique-ified */
 bool
 inner_int_contains(ArrayType *a, ArrayType *b)
@@ -186,37 +188,6 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
-isort_cmp(const void *a, const void *b, void *arg)
-{
-	int32		aval = *((const int32 *) a);
-	int32		bval = *((const int32 *) b);
-
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
-	return 0;
-}
-
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
-
 /* Create a new int array with room for "num" elements */
 ArrayType *
 new_intArrayType(int num)
@@ -306,15 +277,47 @@ internal_size(int *a, int len)
 	return (int) size;
 }
 
+static inline int
+sort_int32_cmp(int32* a, int32* b, bool* ascending)
+{
+	if (*ascending)
+	{
+		if (*a < *b)
+			return -1;
+		if (*a > *b)
+			return 1;
+	}
+	else
+	{
+		if (*a < *b)
+			return 1;
+		if (*a > *b)
+			return -1;
+	}
+	return 0;
+}
+
+#define ST_SORT sort_int32_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) sort_int32_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE bool
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	sort_int32_impl(base, nel, &ascending);
+}
+
 /* unique-ify elements of r in-place ... r must be sorted already */
 ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
 
-	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+	num = qunique(ARRPTR(r), num, sizeof(int), compASC);
 
 	return resize_intArrayType(r, num);
 }
@@ -394,14 +397,8 @@ int_to_intset(int32 elem)
 	return result;
 }
 
-int
+static inline int
 compASC(const void *a, const void *b)
 {
 	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
 }
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
-- 
2.39.5 (Apple Git-154)

v8-0001-Use-specialized-sort-facilities.patchapplication/octet-stream; name=v8-0001-Use-specialized-sort-facilities.patch; x-unix-mode=0644Download
From 3d3c4f48f023e9a3b4d9544db5344edbef75d974 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v8] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 17 +++++-----
 contrib/intarray/_int_tool.c | 66 ++++++++++++++++--------------------
 2 files changed, 38 insertions(+), 45 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..1da2f36f99 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -22,6 +22,8 @@ typedef struct
 	int			num_ranges;		/* number of ranges */
 } GISTIntArrayOptions;
 
+extern void sort_int32(int32 *base, size_t nel, bool ascending);
+
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
 #define ARRNELEMS(x)  ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x))
@@ -42,7 +44,7 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+			sort_int32(ARRPTR(x), _nelems_, true); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
@@ -50,8 +52,10 @@ typedef struct
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
 		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		{ \
+			sort_int32(ARRPTR(x), _nelems_, true); \
+			(x) = _int_unique(x); \
+		} \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +113,6 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +179,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
 		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+			sort_int32(ARRPTR(a), _nelems_, direction); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..4ecb310f9d 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -10,6 +10,9 @@
 #include "common/int.h"
 #include "lib/qunique.h"
 
+static inline int compASC(const void *a, const void *b);
+static inline int compDESC(const void *a, const void *b);
+
 /* arguments are assumed sorted & unique-ified */
 bool
 inner_int_contains(ArrayType *a, ArrayType *b)
@@ -186,37 +189,6 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
-isort_cmp(const void *a, const void *b, void *arg)
-{
-	int32		aval = *((const int32 *) a);
-	int32		bval = *((const int32 *) b);
-
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
-	return 0;
-}
-
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
-
 /* Create a new int array with room for "num" elements */
 ArrayType *
 new_intArrayType(int num)
@@ -306,15 +278,37 @@ internal_size(int *a, int len)
 	return (int) size;
 }
 
+#define ST_SORT sort_int32_asc_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b) compASC(a, b)
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+#define ST_SORT sort_int32_desc_impl
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b) compDESC(a, b)
+#define ST_SCOPE static
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
+
+void sort_int32(int32 *base, size_t nel, bool ascending)
+{
+	if (ascending)
+		sort_int32_asc_impl(base, nel);
+	else
+		sort_int32_desc_impl(base, nel);
+}
+
 /* unique-ify elements of r in-place ... r must be sorted already */
 ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
 
-	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+	num = qunique(ARRPTR(r), num, sizeof(int), compASC);
 
 	return resize_intArrayType(r, num);
 }
@@ -394,13 +388,13 @@ int_to_intset(int32 elem)
 	return result;
 }
 
-int
+static inline int
 compASC(const void *a, const void *b)
 {
 	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
 }
 
-int
+static inline int
 compDESC(const void *a, const void *b)
 {
 	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-- 
2.39.5 (Apple Git-154)

#28John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#27)
1 attachment(s)
Re: Sort functions with specialized comparators

On Sun, Jan 5, 2025 at 1:15 AM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 4 Jan 2025, at 10:24, John Naylor <johncnaylorls@gmail.com> wrote:

v6-0001:

+static int
+unique_cmp(const void *a, const void *b)
+{
+ int32 aval = *((const int32 *) a);
+ int32 bval = *((const int32 *) b);
+
+ return pg_cmp_s32(aval, bval);
+}

I'm not sure it makes sense to create a whole new function for this,
when the same patch removed:

-int
-compASC(const void *a, const void *b)
-{
- return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}

...which in effect the exact same thing.

Otherwise seems close to committable.

I thought about it, but decided to rename the routine.
Here's a version 7 with compASC().

I had the right idea, but the wrong function -- HEAD already had a
suitable comp function, and one that works well with inlined
specialized sorts: isort_cmp(). We just need to remove the extra
argument. Like some other patches in this series, this does have the
side effect of removing the ability to skip quinique(), so that should
be benchmarked (I can do that this week unless you beat me to it). We
can specialize quinique too, as mentioned upthread, but I'm not sure
we need to.

And, just in case, if we already have ASC, why not keep DESC too instead of newly invented cmp function :) PFA v8.

Those functions from common/int.h are probably not good when inlined
(see comment there). If we really want to keep the branch for asc/desc
out of the comparison, it makes more sense to add a function to
reverse the elements after sorting. That allows using the same cmp
function for everything, thus removing the apparent need for a global
wrapper around the static sort function.

I've done both ideas in v9, which also tries to improve patch
legibility by keeping things in the same place they were before. It
also removes the internal "n > 1" checks that you mentioned earlier --
after thinking about it that seems better than adding braces to one
macro to keep it functional.

--
John Naylor
Amazon Web Services

Attachments:

v9-0001-Use-specialized-sort-facilities.patchtext/x-patch; charset=US-ASCII; name=v9-0001-Use-specialized-sort-facilities.patchDownload
From 6edd6da21e38f0301f0a3fdf773381124ad26831 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v9] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 20 +++++------
 contrib/intarray/_int_tool.c | 65 +++++++++++++++++-------------------
 2 files changed, 39 insertions(+), 46 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..5dbf0f66f2 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -41,17 +41,15 @@ typedef struct
 #define SORT(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+		isort(ARRPTR(x), _nelems_); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
 #define PREPAREARR(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		isort(ARRPTR(x), _nelems_); \
+		(x) = _int_unique(x); \
 	} while(0)
 
 /* "wish" function */
@@ -109,12 +107,13 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
+void		isort(int32 *a, size_t len);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
 int			internal_size(int *a, int len);
 ArrayType  *_int_unique(ArrayType *r);
+void		_int_reverse(ArrayType *r);
 int32		intarray_match_first(ArrayType *a, int32 elem);
 ArrayType  *intarray_add_elem(ArrayType *a, int32 elem);
 ArrayType  *intarray_concat_arrays(ArrayType *a, ArrayType *b);
@@ -176,16 +175,13 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+		isort(ARRPTR(a), _nelems_); \
+		if ((direction) == 0) \
+			_int_reverse(a); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..8d3c728c42 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -186,9 +186,9 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
-isort_cmp(const void *a, const void *b, void *arg)
+/* comparison function for isort() and _int_unique() */
+static inline int
+isort_cmp(const void *a, const void *b)
 {
 	int32		aval = *((const int32 *) a);
 	int32		bval = *((const int32 *) b);
@@ -197,25 +197,16 @@ isort_cmp(const void *a, const void *b, void *arg)
 		return -1;
 	if (aval > bval)
 		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
 	return 0;
 }
 
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
+#define ST_SORT isort
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b) isort_cmp(a, b)
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /* Create a new int array with room for "num" elements */
 ArrayType *
@@ -311,14 +302,32 @@ ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
 
-	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+	num = qunique(ARRPTR(r), num, sizeof(int), isort_cmp);
 
 	return resize_intArrayType(r, num);
 }
 
+/* reverse elements of r in place */
+void
+_int_reverse(ArrayType *r)
+{
+	int		   *a = ARRPTR(r);
+	size_t		i = 0;
+	size_t		j = ARRNELEMS(r) - 1;
+
+	while (i < j)
+	{
+		int			tmp;
+
+		tmp = a[i];
+		a[i] = a[j];
+		a[j] = tmp;
+		i++;
+		j--;
+	}
+}
+
 void
 gensign(BITVECP sign, int *a, int len, int siglen)
 {
@@ -393,15 +402,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
-- 
2.47.1

#29Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#28)
Re: Sort functions with specialized comparators

On 6 Jan 2025, at 15:54, John Naylor <johncnaylorls@gmail.com> wrote:

I thought about it, but decided to rename the routine.
Here's a version 7 with compASC().

I had the right idea, but the wrong function -- HEAD already had a
suitable comp function, and one that works well with inlined
specialized sorts: isort_cmp(). We just need to remove the extra
argument. Like some other patches in this series, this does have the
side effect of removing the ability to skip quinique(), so that should
be benchmarked (I can do that this week unless you beat me to it).

With the same setup as in the first message of this thread we can do:

postgres=# SELECT _int_contains(arr,ARRAY[1]) FROM arrays_to_sort;

before patch patch
Time: 567.928 ms
after patch
Time: 890.297 ms
timing of this function is dominated by PREPAREARR(a);

What bothers me is that PREPAREARR(a); is returning new array in case of empty input. That's why I considered little refactoring of resize_intArrayType(): reorder cases so that if (num == ARRNELEMS(a)) was first.

We
can specialize quinique too, as mentioned upthread, but I'm not sure
we need to.

And, just in case, if we already have ASC, why not keep DESC too instead of newly invented cmp function :) PFA v8.

Those functions from common/int.h are probably not good when inlined
(see comment there). If we really want to keep the branch for asc/desc
out of the comparison, it makes more sense to add a function to
reverse the elements after sorting. That allows using the same cmp
function for everything, thus removing the apparent need for a global
wrapper around the static sort function.

I've done both ideas in v9, which also tries to improve patch
legibility by keeping things in the same place they were before. It
also removes the internal "n > 1" checks that you mentioned earlier --
after thinking about it that seems better than adding braces to one
macro to keep it functional.

LGTM.

Thanks!

Best regards, Andrey Borodin.

#30Nathan Bossart
nathandbossart@gmail.com
In reply to: John Naylor (#28)
Re: Sort functions with specialized comparators

On Mon, Jan 06, 2025 at 05:54:29PM +0700, John Naylor wrote:

Those functions from common/int.h are probably not good when inlined
(see comment there).

+1. In fact, I think this comment was added because of the ST_MED3()
function in sort_template.h [0]/messages/by-id/20240212230423.GA3519@nathanxps13. IIRC clang handles this just fine, but
gcc does not.

[0]: /messages/by-id/20240212230423.GA3519@nathanxps13

--
nathan

#31John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#29)
Re: Sort functions with specialized comparators

On Mon, Jan 6, 2025 at 10:51 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

On 6 Jan 2025, at 15:54, John Naylor <johncnaylorls@gmail.com> wrote:

argument. Like some other patches in this series, this does have the
side effect of removing the ability to skip quinique(), so that should
be benchmarked (I can do that this week unless you beat me to it).

With the same setup as in the first message of this thread we can do:

postgres=# SELECT _int_contains(arr,ARRAY[1]) FROM arrays_to_sort;

before patch patch
Time: 567.928 ms
after patch
Time: 890.297 ms
timing of this function is dominated by PREPAREARR(a);

What bothers me is that PREPAREARR(a); is returning new array in case of empty input. That's why I considered little refactoring of resize_intArrayType(): reorder cases so that if (num == ARRNELEMS(a)) was first.

Hmm, I'm confused. First, none of the arrays are empty that I can see
-- am I missing something?

Then, the first message setup is

CREATE TABLE arrays_to_sort AS
SELECT array_shuffle(a) arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

...so most of the time is in sorting the big array, and I don't see a
regression, the opposite in fact:

SELECT _int_contains(arr,ARRAY[1]) FROM arrays_to_sort;

master:
1492.552 ms

v9:
873.697 ms

The case I was concerned about was if the big array was already sorted
and unique. Then, it's conceivable that unnecessarily running qunique
would make things slower, but I don't see that either:

--ordered
CREATE TABLE arrays_sorted AS
SELECT a arr
FROM
(SELECT ARRAY(SELECT generate_series(1, 1000000)) a),
generate_series(1, 10);

SELECT _int_contains(arr,ARRAY[1]) FROM arrays_sorted;

master:
31.388

v9:
28.247

--
John Naylor
Amazon Web Services

#32John Naylor
johncnaylorls@gmail.com
In reply to: Nathan Bossart (#30)
Re: Sort functions with specialized comparators

On Tue, Jan 7, 2025 at 12:47 AM Nathan Bossart <nathandbossart@gmail.com> wrote:

On Mon, Jan 06, 2025 at 05:54:29PM +0700, John Naylor wrote:

Those functions from common/int.h are probably not good when inlined
(see comment there).

+1. In fact, I think this comment was added because of the ST_MED3()
function in sort_template.h [0]. IIRC clang handles this just fine, but
gcc does not.

[0] /messages/by-id/20240212230423.GA3519@nathanxps13

Yeah. If it were just med3, it would probably be okay, but I remember
earlier experiments (also gcc) where branch-free comparators seemed to
not work well with our partitioning scheme.

--
John Naylor
Amazon Web Services

#33Andrey M. Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#31)
Re: Sort functions with specialized comparators

On 7 Jan 2025, at 09:43, John Naylor <johncnaylorls@gmail.com> wrote:

With the same setup as in the first message of this thread we can do:

postgres=# SELECT _int_contains(arr,ARRAY[1]) FROM arrays_to_sort;

before patch patch
Time: 567.928 ms
after patch
Time: 890.297 ms
timing of this function is dominated by PREPAREARR(a);

What bothers me is that PREPAREARR(a); is returning new array in case of empty input. That's why I considered little refactoring of resize_intArrayType(): reorder cases so that if (num == ARRNELEMS(a)) was first.

Hmm, I'm confused. First, none of the arrays are empty that I can see
-- am I missing something?

Ugh...sorry, I posted very confusing results. For starters, I swapped "patched" and "unpatched" results. So results show clear improvement in default case.

I'm worried about another case that we cannot measure: PREPAREARR(a) on empty array will return new array.

And one more case.
BTW for pre-sorted desc arrays desc sorting is slower:
postgres=# CREATE TABLE arrays_sorted_desc AS
SELECT a arr
FROM
(SELECT ARRAY(SELECT -i from generate_series(1, 1000000)i) a),
generate_series(1, 10);
SELECT 10
Time: 707.016 ms
postgres=# SELECT (sort_desc(arr))[0] FROM arrays_sorted_desc;
Time: 41.874 ms

but with a patch
postgres=# SELECT (sort_desc(arr))[0] FROM arrays_sorted_desc;
Time: 166.837 ms

Best regards, Andrey Borodin.

#34John Naylor
johncnaylorls@gmail.com
In reply to: Andrey M. Borodin (#33)

On Tue, Jan 7, 2025 at 12:59 PM Andrey M. Borodin <x4mmm@yandex-team.ru>
wrote:

I'm worried about another case that we cannot measure: PREPAREARR(a) on

empty array will return new array.

In theory, yes, but it doesn't happen in our regression tests, so it might
be worth looking into making that happen before worrying about it further.

https://coverage.postgresql.org/contrib/intarray/_int_tool.c.gcov.html#248

And one more case.
BTW for pre-sorted desc arrays desc sorting is slower:

Right, that's because the sort template special-cases pre-sorted input, and
that only works for descending input if the comparator is wired for
descending output. I'm still not in favor of having two separate
specializations because that's kind of a brute force approach, and even if
that's okay this is a strange place to set that precedent [*]. The
principled way to avoid this regression is to add a one-time check for
descending input in the template, which would be more widely beneficial. I
suspect (and I think the archives show others wondering the same) we could
make the ascending pre-check a one-time operation as well, but I'd need to
test.

[*] It's interesting to note that not terribly long ago isort was an
insertion sort, hence the name:

commit 8d1f239003d0245dda636dfa6cf0add13bee69d6
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun Mar 15 23:22:03 2015 -0400

Replace insertion sort in contrib/intarray with qsort().

--
John Naylor
Amazon Web Services

--
John Naylor
Amazon Web Services

#35John Naylor
johncnaylorls@gmail.com
In reply to: John Naylor (#34)
1 attachment(s)
Re: Sort functions with specialized comparators

I wrote:

On Tue, Jan 7, 2025 at 12:59 PM Andrey M. Borodin <x4mmm@yandex-team.ru> wrote:

And one more case.
BTW for pre-sorted desc arrays desc sorting is slower:

Right, that's because the sort template special-cases pre-sorted input, and that only works for descending input if the comparator is wired for descending output. I'm still not in favor of having two separate specializations because that's kind of a brute force approach, and even if that's okay this is a strange place to set that precedent [*]. The principled way to avoid this regression is to add a one-time check for descending input in the template, which would be more widely beneficial. I suspect (and I think the archives show others wondering the same) we could make the ascending pre-check a one-time operation as well, but I'd need to test.

That's not as clear-cut as I thought. To avoid regressions, I've gone
back to an earlier idea to pass the direction to the comparator, but
this time keep it simple by using the same comparator for sort and
unique, similar to v9.

--
John Naylor
Amazon Web Services

Attachments:

v10-0001-Use-specialized-sort-facilities.patchtext/x-patch; charset=US-ASCII; name=v10-0001-Use-specialized-sort-facilities.patchDownload
From cf2f6056c92380fc2aa9538f6bf80536c1a30a5c Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v10] Use specialized sort facilities

---
 contrib/intarray/_int.h      | 20 +++++-------
 contrib/intarray/_int_tool.c | 62 +++++++++++++++---------------------
 2 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..c92ef13f8e 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -41,17 +41,17 @@ typedef struct
 #define SORT(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+		bool _asc = true; \
+		isort(ARRPTR(x), _nelems_, &_asc); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
 #define PREPAREARR(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		bool _asc = true; \
+		isort(ARRPTR(x), _nelems_, &_asc); \
+		(x) = _int_unique(x); \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +109,7 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
+void		isort(int32 *a, size_t len, void *arg);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +176,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+		bool _asc = (direction) ? true : false; \
+		isort(ARRPTR(a), _nelems_, &_asc); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..6ea189515b 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -186,36 +186,38 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
+/* comparison function for isort() and _int_unique() */
+static inline int
 isort_cmp(const void *a, const void *b, void *arg)
 {
 	int32		aval = *((const int32 *) a);
 	int32		bval = *((const int32 *) b);
 
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
+	if (*((bool *) arg))
+	{
+		if (aval < bval)
+			return -1;
+		if (aval > bval)
+			return 1;
+	}
+	else
+	{
+		if (aval > bval)
+			return -1;
+		if (aval < bval)
+			return 1;
+	}
 	return 0;
 }
 
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
+#define ST_SORT isort
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) isort_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE void
+#define ST_SCOPE
+#define ST_DECLARE
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /* Create a new int array with room for "num" elements */
 ArrayType *
@@ -311,10 +313,10 @@ ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
+	bool		ascending = true;
 
 	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+					  &ascending);
 
 	return resize_intArrayType(r, num);
 }
@@ -393,15 +395,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
-- 
2.47.1

#36Andrey Borodin
x4mmm@yandex-team.ru
In reply to: John Naylor (#35)
Re: Sort functions with specialized comparators

On 14 Jan 2025, at 13:58, John Naylor <johncnaylorls@gmail.com> wrote:

That's not as clear-cut as I thought. To avoid regressions, I've gone
back to an earlier idea to pass the direction to the comparator, but
this time keep it simple by using the same comparator for sort and
unique, similar to v9.

Looks good to me.
Nice stats for some cleaning up 34 insertions(+), 48 deletions(-).

Best regards, Andrey Borodin.

#37John Naylor
johncnaylorls@gmail.com
In reply to: Andrey Borodin (#36)
1 attachment(s)

On Tue, Jan 14, 2025 at 4:22 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Looks good to me.
Nice stats for some cleaning up 34 insertions(+), 48 deletions(-).

Great, I've attached v11 with a draft commit message. It also adds a
comment for the comparator arg and removes ST_DECLARE since we have a
hand-written declaration in the header. I plan to commit this next week
unless there are objections.

--
John Naylor
Amazon Web Services

--
John Naylor
Amazon Web Services

Attachments:

v11-0001-Specialize-intarray-sorting.patchtext/x-patch; charset=US-ASCII; name=v11-0001-Specialize-intarray-sorting.patchDownload
From 868506ebef1cd20aee053e4767aac22cd40b1da0 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@night.local>
Date: Sat, 18 May 2024 23:02:50 +0500
Subject: [PATCH v11] Specialize intarray sorting

There is at least one report in the field of storing millions of
integers in arrays, so it seems like a good time to specialize
intarray's qsort function. In doing so, streamline the comparators:
Previously there were three, two for each direction for sorting
and one passed to qunique_arg. To preserve the early exit in the
case of descending input, pass the direction as an argument to
the comparator. This requires giving up duplicate detection, which
previously allowed skipping the unique-ifying step. Testing showed
no regressions in always calling qunique().

In passing, get rid of nearby checks that the input has at least
two elements, since preserving them would make some macros less
readable. These are not necessary for correctness, and seem like
premature optimizations.

Author: Andrey M. Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/098A3E67-E4A6-4086-9C66-B1EAEB1DFE1C@yandex-team.ru
---
 contrib/intarray/_int.h      | 20 +++++-------
 contrib/intarray/_int_tool.c | 62 +++++++++++++++---------------------
 2 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index 0352cbd368..c92ef13f8e 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -41,17 +41,17 @@ typedef struct
 #define SORT(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			isort(ARRPTR(x), _nelems_); \
+		bool _asc = true; \
+		isort(ARRPTR(x), _nelems_, &_asc); \
 	} while(0)
 
 /* sort the elements of the array and remove duplicates */
 #define PREPAREARR(x) \
 	do { \
 		int		_nelems_ = ARRNELEMS(x); \
-		if (_nelems_ > 1) \
-			if (isort(ARRPTR(x), _nelems_)) \
-				(x) = _int_unique(x); \
+		bool _asc = true; \
+		isort(ARRPTR(x), _nelems_, &_asc); \
+		(x) = _int_unique(x); \
 	} while(0)
 
 /* "wish" function */
@@ -109,7 +109,7 @@ typedef struct
 /*
  * useful functions
  */
-bool		isort(int32 *a, int len);
+void		isort(int32 *a, size_t len, void *arg);
 ArrayType  *new_intArrayType(int num);
 ArrayType  *copy_intArrayType(ArrayType *a);
 ArrayType  *resize_intArrayType(ArrayType *a, int num);
@@ -176,16 +176,12 @@ bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
 bool		query_has_required_values(QUERYTYPE *query);
 
-int			compASC(const void *a, const void *b);
-int			compDESC(const void *a, const void *b);
-
 /* sort, either ascending or descending */
 #define QSORT(a, direction) \
 	do { \
 		int		_nelems_ = ARRNELEMS(a); \
-		if (_nelems_ > 1) \
-			qsort((void*) ARRPTR(a), _nelems_, sizeof(int32), \
-				  (direction) ? compASC : compDESC ); \
+		bool _asc = (direction) ? true : false; \
+		isort(ARRPTR(a), _nelems_, &_asc); \
 	} while(0)
 
 #endif							/* ___INT_H__ */
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index c85280c842..0d0cdc289c 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -186,36 +186,38 @@ rt__int_size(ArrayType *a, float *size)
 	*size = (float) ARRNELEMS(a);
 }
 
-/* qsort_arg comparison function for isort() */
-static int
+/* comparison function for isort() and _int_unique() */
+static inline int
 isort_cmp(const void *a, const void *b, void *arg)
 {
 	int32		aval = *((const int32 *) a);
 	int32		bval = *((const int32 *) b);
 
-	if (aval < bval)
-		return -1;
-	if (aval > bval)
-		return 1;
-
-	/*
-	 * Report if we have any duplicates.  If there are equal keys, qsort must
-	 * compare them at some point, else it wouldn't know whether one should go
-	 * before or after the other.
-	 */
-	*((bool *) arg) = true;
+	if (*((bool *) arg))
+	{
+		/* compare for ascending order */
+		if (aval < bval)
+			return -1;
+		if (aval > bval)
+			return 1;
+	}
+	else
+	{
+		if (aval > bval)
+			return -1;
+		if (aval < bval)
+			return 1;
+	}
 	return 0;
 }
 
-/* Sort the given data (len >= 2).  Return true if any duplicates found */
-bool
-isort(int32 *a, int len)
-{
-	bool		r = false;
-
-	qsort_arg(a, len, sizeof(int32), isort_cmp, &r);
-	return r;
-}
+#define ST_SORT isort
+#define ST_ELEMENT_TYPE int32
+#define ST_COMPARE(a, b, ascending) isort_cmp(a, b, ascending)
+#define ST_COMPARE_ARG_TYPE void
+#define ST_SCOPE
+#define ST_DEFINE
+#include "lib/sort_template.h"
 
 /* Create a new int array with room for "num" elements */
 ArrayType *
@@ -311,10 +313,10 @@ ArrayType *
 _int_unique(ArrayType *r)
 {
 	int			num = ARRNELEMS(r);
-	bool		duplicates_found;	/* not used */
+	bool		ascending = true;
 
 	num = qunique_arg(ARRPTR(r), num, sizeof(int), isort_cmp,
-					  &duplicates_found);
+					  &ascending);
 
 	return resize_intArrayType(r, num);
 }
@@ -393,15 +395,3 @@ int_to_intset(int32 elem)
 	aa[0] = elem;
 	return result;
 }
-
-int
-compASC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) a, *(const int32 *) b);
-}
-
-int
-compDESC(const void *a, const void *b)
-{
-	return pg_cmp_s32(*(const int32 *) b, *(const int32 *) a);
-}
-- 
2.48.1

#38John Naylor
johncnaylorls@gmail.com
In reply to: John Naylor (#37)
Re: Sort functions with specialized comparators

On Wed, Feb 5, 2025 at 8:34 PM John Naylor <johncnaylorls@gmail.com> wrote:

On Tue, Jan 14, 2025 at 4:22 PM Andrey Borodin <x4mmm@yandex-team.ru> wrote:

Looks good to me.
Nice stats for some cleaning up 34 insertions(+), 48 deletions(-).

Great, I've attached v11 with a draft commit message. It also adds a comment for the comparator arg and removes ST_DECLARE since we have a hand-written declaration in the header. I plan to commit this next week unless there are objections.

Committed, with just one small change of "_asc" to "_ascending" for readability.

--
John Naylor
Amazon Web Services