]> mj.ucw.cz Git - libucw.git/commitdiff
More bits of the array sorter: radix-sort implemented.
authorMartin Mares <mj@ucw.cz>
Thu, 6 Sep 2007 19:27:31 +0000 (21:27 +0200)
committerMartin Mares <mj@ucw.cz>
Thu, 6 Sep 2007 19:27:31 +0000 (21:27 +0200)
lib/sorter/Makefile
lib/sorter/array.c [new file with mode: 0644]
lib/sorter/array.h
lib/sorter/common.h
lib/sorter/s-fixint.h
lib/sorter/s-internal.h
lib/sorter/sorter.h

index 6a0c4f94117e4db12d8ca74a800e990a703835df..7dfa34dea5a7420dad8a6af5e01ba2ebc971f5a1 100644 (file)
@@ -2,7 +2,7 @@
 
 DIRS+=lib/sorter
 
-LIBUCW_MODS+=sorter/config sorter/govern sorter/sbuck
+LIBUCW_MODS+=$(addprefix sorter/, config govern sbuck array)
 PROGS+=$(o)/lib/sorter/sort-test $(o)/lib/sorter/old-test
 
 $(o)/lib/sorter/sort-test: $(o)/lib/sorter/sort-test.o $(LIBUCW)
diff --git a/lib/sorter/array.c b/lib/sorter/array.c
new file mode 100644 (file)
index 0000000..65a1192
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *     UCW Library -- Optimized Array Sorter
+ *
+ *     (c) 2003--2007 Martin Mares <mj@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+#define LOCAL_DEBUG
+
+#include "lib/lib.h"
+#include "lib/sorter/common.h"
+
+#include <string.h>
+
+#define ASORT_MIN_RADIX 5000           // FIXME: var?
+#define ASORT_MIN_SHIFT 2
+
+static void
+asort_radix(struct asort_context *ctx, void *array, void *buffer, uns num_elts, uns hash_bits, uns swapped_output)
+{
+  uns buckets = (1 << ctx->radix_bits);
+  uns shift = (hash_bits > ctx->radix_bits) ? (hash_bits - ctx->radix_bits) : 0;
+  uns cnt[buckets];
+
+  DBG(">>> n=%d h=%d s=%d sw=%d", num_elts, hash_bits, shift, swapped_output);
+
+  bzero(cnt, sizeof(cnt));
+  ctx->radix_count(array, num_elts, cnt, shift);
+
+  uns pos = 0;
+  for (uns i=0; i<buckets; i++)
+    {
+      uns j = cnt[i];
+      cnt[i] = pos;
+      pos += j;
+    }
+  ASSERT(pos == num_elts);
+
+  ctx->radix_split(array, buffer, num_elts, cnt, shift);
+  pos = 0;
+  for (uns i=0; i<buckets; i++)
+    {
+      uns n = cnt[i] - pos;
+      if (n < ASORT_MIN_RADIX || shift < ASORT_MIN_SHIFT)
+       {
+         ctx->quicksort(buffer, n);
+         if (!swapped_output)
+           memcpy(array, buffer, n * ctx->elt_size);
+       }
+      else
+       asort_radix(ctx, buffer, array, n, shift, !swapped_output);
+      array += n * ctx->elt_size;
+      buffer += n * ctx->elt_size;
+      pos = cnt[i];
+    }
+}
+
+void
+asort_run(struct asort_context *ctx)
+{
+  SORT_XTRACE(10, "Array-sorting %d items per %d bytes, hash_bits=%d", ctx->num_elts, ctx->elt_size, ctx->hash_bits);
+
+  if (ctx->num_elts < ASORT_MIN_RADIX ||
+      ctx->hash_bits <= ASORT_MIN_SHIFT ||
+      !ctx->radix_split ||
+      (sorter_debug & SORT_DEBUG_ASORT_NO_RADIX))
+    {
+      SORT_XTRACE(12, "Decided to use direct quicksort");
+      ctx->quicksort(ctx->array, ctx->num_elts);
+    }
+  else
+    {
+      SORT_XTRACE(12, "Decided to use radix-sort");
+      // FIXME: select dest buffer
+      asort_radix(ctx, ctx->array, ctx->buffer, ctx->num_elts, ctx->hash_bits, 0);
+    }
+
+  SORT_XTRACE(11, "Array-sort finished");
+}
index 73da6908df5af00f4182fb3c046f05b5ee6110cb..1370baa5b8901b7c2c9cfe19530d80c100f6bc9e 100644 (file)
@@ -22,8 +22,9 @@
  *  ASORT_KEY_TYPE  [*]        data type of a single array entry key
  *  ASORT_LT(x,y)      x < y for ASORT_TYPE (default: "x<y")
  *  ASORT_THRESHOLD    threshold for switching between quicksort and insertsort
- *  ASORT_PAGE_ALIGNED the array is guaranteed to be aligned to a multiple of CPU_PAGE_SIZE
+ *  ASORT_PAGE_ALIGNED the array is guaranteed to be aligned to a multiple of CPU_PAGE_SIZE  (FIXME: Do we need this?)
  *  ASORT_HASH(x)      FIXME
+ *  ASORT_RADIX_BITS   FIXME
  *  ASORT_SWAP         FIXME: probably keep private
  *
  *  After including this file, a function
@@ -49,20 +50,26 @@ typedef ASORT_KEY_TYPE Q(key);
 #define ASORT_THRESHOLD 8              /* Guesswork and experimentation */
 #endif
 
-static void Q(raw_sort)(Q(key) *array, uns array_size)
+#ifndef ASORT_RADIX_BITS
+#define ASORT_RADIX_BITS 10            // FIXME: Tune automatically?
+#endif
+#define ASORT_RADIX_MASK ((1 << (ASORT_RADIX_BITS)) - 1)
+
+static void Q(quicksort)(void *array_ptr, uns num_elts)
 {
+  Q(key) *array = array_ptr;
   struct stk { int l, r; } stack[8*sizeof(uns)];
   int l, r, left, right, m;
   uns sp = 0;
   Q(key) pivot;
 
-  if (array_size <= 1)
+  if (num_elts <= 1)
     return;
 
   /* QuickSort with optimizations a'la Sedgewick, but stop at ASORT_THRESHOLD */
 
   left = 0;
-  right = array_size - 1;
+  right = num_elts - 1;
   for(;;)
     {
       l = left;
@@ -140,7 +147,7 @@ static void Q(raw_sort)(Q(key) *array, uns array_size)
    */
 
   /* Find minimal element which will serve as a barrier */
-  r = MIN(array_size, ASORT_THRESHOLD);
+  r = MIN(num_elts, ASORT_THRESHOLD);
   m = 0;
   for (l=1; l<r; l++)
     if (ASORT_LT(array[l], array[m]))
@@ -148,7 +155,7 @@ static void Q(raw_sort)(Q(key) *array, uns array_size)
   ASORT_SWAP(0,m);
 
   /* Insertion sort */
-  for (m=1; m<(int)array_size; m++)
+  for (m=1; m<(int)num_elts; m++)
     {
       l=m;
       while (ASORT_LT(array[m], array[l-1]))
@@ -161,12 +168,41 @@ static void Q(raw_sort)(Q(key) *array, uns array_size)
     }
 }
 
+#ifdef ASORT_HASH
+
+static void Q(radix_count)(void *src_ptr, uns num_elts, uns *cnt, uns shift)
+{
+  Q(key) *src = src_ptr;
+  for (uns i=0; i<num_elts; i++)
+    cnt[ (ASORT_HASH(src[i]) >> shift) & ASORT_RADIX_MASK ] ++;
+}
+
+static void Q(radix_split)(void *src_ptr, void *dest_ptr, uns num_elts, uns *ptrs, uns shift)
+{
+  Q(key) *src = src_ptr, *dest = dest_ptr;
+  for (uns i=0; i<num_elts; i++)
+    dest[ ptrs[ (ASORT_HASH(src[i]) >> shift) & ASORT_RADIX_MASK ]++ ] = src[i];
+}
+
+#endif
+
 static Q(key) *Q(sort)(Q(key) *array, uns num_elts, Q(key) *buffer, uns hash_bits)
 {
-  (void) buffer;
-  (void) hash_bits;
-  Q(raw_sort)(array, num_elts);
-  return array;
+  struct asort_context ctx = {
+    .array = array,
+    .buffer = buffer,
+    .num_elts = num_elts,
+    .hash_bits = hash_bits,
+    .elt_size = sizeof(Q(key)),
+    .quicksort = Q(quicksort),
+#ifdef ASORT_HASH
+    .radix_count = Q(radix_count),
+    .radix_split = Q(radix_split),
+    .radix_bits = ASORT_RADIX_BITS,
+#endif
+  };
+  asort_run(&ctx);
+  return ctx.array;
 }
 
 /* FIXME */
@@ -177,4 +213,6 @@ static Q(key) *Q(sort)(Q(key) *array, uns num_elts, Q(key) *buffer, uns hash_bit
 #undef ASORT_THRESHOLD
 #undef ASORT_PAGE_ALIGNED
 #undef ASORT_HASH
+#undef ASORT_RADIX_BITS
+#undef ASORT_RADIX_MASK
 #undef Q
index 70e10c08f84a620dc19a4c6d10e9fff9b226d8b8..b8438ff75e4289a19c2b097e7b42701964101068 100644 (file)
@@ -28,6 +28,8 @@ enum sort_debug {
   SORT_DEBUG_KEEP_BUCKETS = 4,
   SORT_DEBUG_NO_RADIX = 8,
   SORT_DEBUG_NO_MULTIWAY = 16,
+  SORT_DEBUG_ASORT_NO_RADIX = 32,
+  SORT_DEBUG_ASORT_NO_THREADS = 64
 };
 
 struct sort_bucket;
@@ -114,4 +116,20 @@ struct fastbuf *sbuck_read(struct sort_bucket *b);
 struct fastbuf *sbuck_write(struct sort_bucket *b);
 void sbuck_swap_out(struct sort_bucket *b);
 
+/* Contexts and helper functions for the array sorter */
+
+struct asort_context {
+  void *array;                         // Array to sort
+  void *buffer;                                // Auxiliary buffer (required when radix-sorting)
+  uns num_elts;                                // Number of elements in the array
+  uns elt_size;                                // Bytes per element
+  uns hash_bits;                       // Remaining bits of hash function
+  uns radix_bits;                      // How many bits to process in a single radix-sort pass
+  void (*quicksort)(void *array_ptr, uns num_elts);
+  void (*radix_count)(void *src_ptr, uns num_elts, uns *cnt, uns shift);
+  void (*radix_split)(void *src_ptr, void *dest_ptr, uns num_elts, uns *ptrs, uns shift);
+};
+
+void asort_run(struct asort_context *ctx);
+
 #endif
index 6eac13bb49780fc5c78c5d8daa4796d94fcc7ee6..7aafa7de8965dce8e096b9fc8a256b21a3c121fa 100644 (file)
@@ -13,6 +13,9 @@
 #define ASORT_KEY_TYPE P(key)
 #define ASORT_LT(x,y) (P(compare)(&(x), &(y)) < 0)
 #define ASORT_PAGE_ALIGNED
+#ifdef SORT_INTERNAL_RADIX
+#define ASORT_HASH(x) P(hash)(&(x))
+#endif
 #include "lib/sorter/array.h"
 
 /*
@@ -30,7 +33,7 @@ static size_t P(internal_workspace)(void)
 #ifdef SORT_UNIFY
   workspace = sizeof(P(key) *);
 #endif
-#ifdef SORT_HASH_BITS  // FIXME: Another switch?
+#ifdef SORT_INTERNAL_RADIX
   workspace = MAX(workspace, sizeof(P(key)));
 #endif
   return workspace;
@@ -68,7 +71,7 @@ static int P(internal)(struct sort_context *ctx, struct sort_bucket *bin, struct
   timestamp_t timer;
   init_timer(&timer);
   buf = P(array_sort)(buf, n,
-#ifdef SORT_HASH_BITS
+#ifdef SORT_INTERNAL_RADIX
     workspace, bin->hash_bits
 #else
     NULL, 0
index 92a3ad64a8191409e5af1691f88b3a57b626b5d1..85557f126544aa93d49f3ddce1ce6acd82aac343 100644 (file)
@@ -65,7 +65,7 @@ static inline size_t P(internal_workspace)(P(key) *key UNUSED)
 #ifdef SORT_UNIFY_WORKSPACE
   ws += SORT_UNIFY_WORKSPACE(*key);
 #endif
-#ifdef SORT_HASH_BITS
+#ifdef SORT_INTERNAL_RADIX
   ws = MAX(ws, sizeof(P(internal_item_t)));
 #endif
   return ws;
@@ -146,7 +146,7 @@ static int P(internal)(struct sort_context *ctx, struct sort_bucket *bin, struct
   timestamp_t timer;
   init_timer(&timer);
   item_array = P(array_sort)(item_array, count,
-#ifdef SORT_HASH_BITS
+#ifdef SORT_INTERNAL_RADIX
     workspace, bin->hash_bits
 #else
     NULL, 0
index 675a3d5b14fd21a0dbf5bd79f678c2a4e119e24c..5a009fabbf5f502614efdcf008d2708a4bc83931 100644 (file)
@@ -193,6 +193,11 @@ static inline void P(copy_merged)(P(key) **keys, struct fastbuf **data UNUSED, u
 }
 #endif
 
+#if defined(SORT_HASH_BITS) || defined(SORT_INT)
+#define SORT_INTERNAL_RADIX
+#include "lib/sorter/s-radix.h"
+#endif
+
 #if defined(SORT_VAR_KEY) || defined(SORT_VAR_DATA) || defined(SORT_UNIFY_WORKSPACE)
 #include "lib/sorter/s-internal.h"
 #else
@@ -202,10 +207,6 @@ static inline void P(copy_merged)(P(key) **keys, struct fastbuf **data UNUSED, u
 #include "lib/sorter/s-twoway.h"
 #include "lib/sorter/s-multiway.h"
 
-#if defined(SORT_HASH_BITS) || defined(SORT_INT)
-#include "lib/sorter/s-radix.h"
-#endif
-
 static struct fastbuf *P(sort)(
 #ifdef SORT_INPUT_FILE
                               byte *in,
@@ -301,6 +302,7 @@ static struct fastbuf *P(sort)(
 #undef SORT_UNIQUE
 #undef SORT_ASSERT_UNIQUE
 #undef SORT_DELETE_INPUT
+#undef SORT_INTERNAL_RADIX
 #undef SWAP
 #undef LESS
 #undef P