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)
--- /dev/null
+/*
+ * 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");
+}
* 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
#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;
*/
/* 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]))
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]))
}
}
+#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 */
#undef ASORT_THRESHOLD
#undef ASORT_PAGE_ALIGNED
#undef ASORT_HASH
+#undef ASORT_RADIX_BITS
+#undef ASORT_RADIX_MASK
#undef Q
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;
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
#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"
/*
#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;
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
#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;
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
}
#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
#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,
#undef SORT_UNIQUE
#undef SORT_ASSERT_UNIQUE
#undef SORT_DELETE_INPUT
+#undef SORT_INTERNAL_RADIX
#undef SWAP
#undef LESS
#undef P