SORT_DEBUG_NO_PRESORT = 1,
SORT_DEBUG_NO_JOIN = 2,
SORT_DEBUG_KEEP_BUCKETS = 4,
+ SORT_DEBUG_NO_RADIX = 8,
};
struct sort_bucket;
// Two-way split/merge: merge up to 2 source buckets to up to 2 destination buckets.
// Bucket arrays are NULL-terminated.
void (*twoway_merge)(struct sort_context *ctx, struct sort_bucket **ins, struct sort_bucket **outs);
+ // Radix split according to hash function
+ void (*radix_split)(struct sort_context *ctx, struct sort_bucket *in, struct sort_bucket **outs, uns bitpos, uns numbits);
// State variables of internal_sort
void *key_buf;
ins[0] = sbuck_new(ctx);
if (!sorter_presort(ctx, b, ins[0], join ? : ins[0]))
{
- SORT_TRACE("Sorted in memory");
+ SORT_XTRACE(((b->flags & SBF_SOURCE) ? 1 : 2), "Sorted in memory");
if (join)
sbuck_drop(ins[0]);
else
clist_insert_after(&ins[0]->n, list_pos);
}
+static int
+sorter_radix_p(struct sort_context *ctx, struct sort_bucket *b)
+{
+ return b->hash_bits && ctx->radix_split &&
+ !(sorter_debug & SORT_DEBUG_NO_RADIX) &&
+ sbuck_size(b) > (sh_off_t)sorter_bufsize;
+}
+
+static void
+sorter_radix(struct sort_context *ctx, struct sort_bucket *b)
+{
+ uns bits = MIN(b->hash_bits, 4); /* FIXME */
+ uns nbuck = 1 << bits;
+ SORT_XTRACE(2, "Running radix sort on %s with %d bits of %d", F_BSIZE(b), bits, b->hash_bits);
+ sorter_start_timer(ctx);
+
+ struct sort_bucket *outs[nbuck];
+ for (uns i=nbuck; i--; )
+ {
+ outs[i] = sbuck_new(ctx);
+ outs[i]->hash_bits = b->hash_bits - bits;
+ clist_insert_after(&outs[i]->n, &b->n);
+ }
+
+ ctx->radix_split(ctx, b, outs, b->hash_bits - bits, bits);
+
+ u64 min = ~0U, max = 0, sum = 0;
+ for (uns i=0; i<nbuck; i++)
+ {
+ u64 s = sbuck_size(outs[i]);
+ min = MIN(min, s);
+ max = MAX(max, s);
+ sum += s;
+ }
+
+ SORT_TRACE("Radix split (%d buckets, %s min, %s max, %s avg, %dMB/s)", nbuck,
+ F_SIZE(min), F_SIZE(max), F_SIZE(sum / nbuck), sorter_speed(ctx, sum));
+ sbuck_drop(b);
+}
+
void
sorter_run(struct sort_context *ctx)
{
else
bin->fb = ctx->in_fb;
bin->ident = "in";
- bin->size = ctx->in_size;
+ bin->size = ctx->in_size; /* Sizes should be either sh_off_t or u64, not both; beware of ~0U */
bin->hash_bits = ctx->hash_bits;
clist_add_tail(&ctx->bucket_list, &bin->n);
SORT_XTRACE(2, "Input size: %s", (ctx->in_size == ~(u64)0 ? (byte*)"unknown" : F_BSIZE(bin)));
sbuck_drop(b);
else if (b->runs == 1)
sorter_join(b);
+ else if (sorter_radix_p(ctx, b))
+ sorter_radix(ctx, b);
else
sorter_twoway(ctx, b);
}
--- /dev/null
+/*
+ * UCW Library -- Universal Sorter: Radix-Split Module
+ *
+ * (c) 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.
+ */
+
+/* FIXME: This is a very trivial implementation so far. Use fbdirect and such things to speed up. */
+
+#include <string.h>
+
+static void P(radix_split)(struct sort_context *ctx UNUSED, struct sort_bucket *bin, struct sort_bucket **bouts, uns bitpos, uns numbits)
+{
+ uns nbucks = 1 << numbits;
+ uns mask = nbucks - 1;
+ struct fastbuf *in = sbuck_read(bin);
+ P(key) k;
+
+ struct fastbuf *outs[nbucks];
+ bzero(outs, sizeof(outs));
+
+ while (P(read_key)(in, &k))
+ {
+ uns h = P(hash)(&k);
+ uns i = (h >> bitpos) & mask;
+ if (unlikely(!outs[i]))
+ outs[i] = sbuck_write(bouts[i]);
+ P(copy_data)(&k, in, outs[i]);
+ }
+}
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
+#include <unistd.h>
/*** Time measurement ***/
static void
start(void)
{
+ sync();
init_timer();
}
static void
stop(void)
{
+ sync();
log(L_INFO, "Test took %.3fs", get_timer() / 1000.);
}
* SORT_HASH_BITS signals that a monotone hashing function returning a given number of
* bits is available. Monotone hash is a function f such that f(x) < f(y)
* implies x < y and which is approximately uniformly distributed.
- * uns PREFIX_hash(SORT_KEY *a, SORT_KEY *b)
+ * uns PREFIX_hash(SORT_KEY *a)
*
* Unification:
*
#include "lib/sorter/s-internal.h"
#include "lib/sorter/s-twoway.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,
#ifdef SORT_HASH_BITS
ctx.hash_bits = SORT_HASH_BITS;
+ ctx.radix_split = P(radix_split);
#elif defined(SORT_INT)
ctx.hash_bits = 0;
while (ctx.hash_bits < 32 && (int_range >> ctx.hash_bits))
ctx.hash_bits++;
+ ctx.radix_split = P(radix_split);
#endif
ctx.internal_sort = P(internal);