From 2e864b5a7ca3ba94399c386b07e0258b85d6a7b0 Mon Sep 17 00:00:00 2001
From: alyapunov <aleks@tarantool.org>
Date: Tue, 4 Jul 2017 17:39:59 +0300
Subject: [PATCH] Use open MP sort optimisation only for huge arrays

Now open MP sort is used for any size of an array.
For small arrays it's an overkill and even can cause overhead
due to thread pool creation.

Invoke open MP sort only for big arrays and use old good
single-thread qsort for small arrays.

Fix #2431
---
 cmake/BuildMisc.cmake      |  4 +---
 third_party/qsort_arg.c    | 45 ++++++++++++++++++++++++++++++++++++--
 third_party/qsort_arg.h    |  8 ++++++-
 third_party/qsort_arg_mt.c |  8 +++++--
 4 files changed, 57 insertions(+), 8 deletions(-)

diff --git a/cmake/BuildMisc.cmake b/cmake/BuildMisc.cmake
index 2f88432ece..71c146f961 100644
--- a/cmake/BuildMisc.cmake
+++ b/cmake/BuildMisc.cmake
@@ -6,6 +6,7 @@ macro(libmisc_build)
         ${PROJECT_SOURCE_DIR}/third_party/sha1.c
         ${PROJECT_SOURCE_DIR}/third_party/PMurHash.c
         ${PROJECT_SOURCE_DIR}/third_party/base64.c
+        ${PROJECT_SOURCE_DIR}/third_party/qsort_arg.c
     )
 
     if (CC_HAS_WNO_IMPLICIT_FALLTHROUGH)
@@ -35,9 +36,6 @@ macro(libmisc_build)
     if (HAVE_OPENMP)
         list(APPEND misc_src
              ${PROJECT_SOURCE_DIR}/third_party/qsort_arg_mt.c)
-    else()
-        list(APPEND misc_src
-             ${PROJECT_SOURCE_DIR}/third_party/qsort_arg.c)
     endif()
 
     add_library(misc STATIC ${misc_src})
diff --git a/third_party/qsort_arg.c b/third_party/qsort_arg.c
index 5d0b7ae1c6..4f0cf5adf5 100644
--- a/third_party/qsort_arg.c
+++ b/third_party/qsort_arg.c
@@ -50,6 +50,16 @@
 #include <third_party/qsort_arg.h>
 #include <stdint.h>
 
+enum {
+	/*
+	 * The size of an array from which it is reasonable to start
+	 * multithread sort.
+	 * If the array size is less than the size below, a single-threaded
+	 * version of qsort will be started.S
+	 */
+	MULTITHREAD_SIZE_THRESHOLD = 128 * 1024,
+};
+
 #define min(a, b)   (a) < (b) ? a : b
 
 static char *med3(char *a, char *b, char *c,
@@ -105,8 +115,11 @@ med3(char *a, char *b, char *c, int (*cmp)(const void *a, const void *b, void *a
 		: (cmp(b, c, arg) > 0 ? b : (cmp(a, c, arg) < 0 ? a : c));
 }
 
-void
-qsort_arg(void *a, size_t n, size_t es, int (*cmp)(const void *a, const void *b, void *arg), void *arg)
+/**
+ * Single-thread version of qsort.
+ */
+static void
+qsort_arg_st(void *a, size_t n, size_t es, int (*cmp)(const void *a, const void *b, void *arg), void *arg)
 {
 	char	   *pa,
 			   *pb,
@@ -199,3 +212,31 @@ loop:SWAPINIT(a, es);
 	}
 /*		qsort_arg(pn - r, r / es, es, cmp, arg);*/
 }
+
+#ifdef HAVE_OPENMP
+/**
+ * Multi-thread version of qsort. Only present when target machine supports
+ * open MP.
+ */
+void qsort_arg_mt(void *a, size_t n, size_t es,
+		  int (*cmp)(const void *, const void *, void *), void *arg);
+#endif
+
+/**
+ * General version of qsort that calls single-threaded of multi-threaded
+ * qsort depending on open MP availability and given array size.
+ */
+void
+qsort_arg(void *a, size_t n, size_t es,
+	  int (*cmp)(const void *a, const void *b, void *arg), void *arg)
+{
+#ifdef HAVE_OPENMP
+	if (n >= MULTITHREAD_SIZE_THRESHOLD)
+		qsort_arg_mt(a, n, es, cmp, arg);
+	else
+		qsort_arg_st(a, n, es, cmp, arg);
+#else
+	qsort_arg_st(a, n, es, cmp, arg);
+#endif
+}
+
diff --git a/third_party/qsort_arg.h b/third_party/qsort_arg.h
index 6b9fe44cc0..a96e34be68 100644
--- a/third_party/qsort_arg.h
+++ b/third_party/qsort_arg.h
@@ -1,13 +1,19 @@
 #ifndef QSORT_ARG_H
 #define QSORT_ARG_H
 
+#include <trivia/config.h>
 #include <sys/types.h>
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
-void qsort_arg(void *a, size_t n, size_t es, int (*cmp)(const void *a, const void *b, void *arg), void *arg);
+/**
+ * General version of qsort that calls single-threaded of multi-threaded
+ * qsort depending on open MP availability and given array size.
+ */
+void qsort_arg(void *a, size_t n, size_t es,
+	       int (*cmp)(const void *a, const void *b, void *arg), void *arg);
 
 #if defined(__cplusplus)
 }
diff --git a/third_party/qsort_arg_mt.c b/third_party/qsort_arg_mt.c
index 71ccae3702..21c9ee58a9 100644
--- a/third_party/qsort_arg_mt.c
+++ b/third_party/qsort_arg_mt.c
@@ -54,6 +54,10 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+#ifndef HAVE_OPENMP
+#error "HAVE_OPENMP macro is not defined"
+#endif
+
 #define min(a, b)   (a) < (b) ? a : b
 
 static char *med3(char *a, char *b, char *c,
@@ -218,8 +222,8 @@ qsort_arg_mt_internal(void *a, size_t n, intptr_t es,
 }
 
 void
-qsort_arg(void *a, size_t n, size_t es,
-	  int (*cmp)(const void *a, const void *b, void *arg), void *arg)
+qsort_arg_mt(void *a, size_t n, size_t es,
+	     int (*cmp)(const void *a, const void *b, void *arg), void *arg)
 {
 #pragma omp parallel
 	{
-- 
GitLab