]> mj.ucw.cz Git - libucw.git/commitdiff
Added decision logic which switches between 2-way merges, n-way merges
authorMartin Mares <mj@ucw.cz>
Fri, 31 Aug 2007 12:57:49 +0000 (14:57 +0200)
committerMartin Mares <mj@ucw.cz>
Fri, 31 Aug 2007 12:57:49 +0000 (14:57 +0200)
and radix splits.

lib/sorter/common.h
lib/sorter/config.c
lib/sorter/govern.c
lib/sorter/sbuck.c

index b4b24ab865af01890d71552d368f702a47750389..70e10c08f84a620dc19a4c6d10e9fff9b226d8b8 100644 (file)
@@ -15,6 +15,7 @@
 /* Configuration, some of the variables are used by the old sorter, too. */
 extern uns sorter_trace, sorter_presort_bufsize, sorter_stream_bufsize;
 extern uns sorter_debug, sorter_min_radix_bits, sorter_max_radix_bits;
+extern uns sorter_min_multiway_bits, sorter_max_multiway_bits;
 extern u64 sorter_bufsize;
 extern struct fb_params sorter_fb_params;
 
index 8708f7e457eb1df0669c6bdba8d411e3bca9032c..a4bfc67bf90fb5bbf0698aa44803f887f5b16e95 100644 (file)
@@ -19,6 +19,8 @@ u64 sorter_bufsize = 65536;
 uns sorter_debug;
 uns sorter_min_radix_bits;
 uns sorter_max_radix_bits;
+uns sorter_min_multiway_bits;
+uns sorter_max_multiway_bits;
 struct fb_params sorter_fb_params;
 
 static struct cf_section sorter_config = {
@@ -31,6 +33,8 @@ static struct cf_section sorter_config = {
     CF_UNS("Debug", &sorter_debug),
     CF_UNS("MinRadixBits", &sorter_min_radix_bits),
     CF_UNS("MaxRadixBits", &sorter_max_radix_bits),
+    CF_UNS("MinMultiwayBits", &sorter_min_multiway_bits),
+    CF_UNS("MaxMultiwayBits", &sorter_max_multiway_bits),
     CF_END
   }
 };
index 0d80c2985067022e9dff0db44d1f85efdef758f3..5173f2967071a44b0295b294bd9dac2e722f3e3a 100644 (file)
@@ -187,7 +187,6 @@ sorter_multiway(struct sort_context *ctx, struct sort_bucket *b)
 
   clist_init(&parts);
   ASSERT(!(sorter_debug & SORT_DEBUG_NO_PRESORT));
-  // FIXME: What if the parts will be too small?
   SORT_XTRACE(3, "%s", ((b->flags & SBF_CUSTOM_PRESORT) ? "Custom presorting" : "Presorting"));
   uns cont;
   uns part_cnt = 0;
@@ -209,6 +208,7 @@ sorter_multiway(struct sort_context *ctx, struct sort_bucket *b)
     }
   while (cont);
   sorter_stop_timer(ctx, &ctx->total_pre_time);
+  sorter_free_buf(ctx);
   sbuck_drop(b);
 
   // FIXME: This is way too similar to the two-way case.
@@ -232,7 +232,7 @@ sorter_multiway(struct sort_context *ctx, struct sort_bucket *b)
 
   SORT_TRACE("Multi-way presorting pass (%d parts, %s, %dMB/s)", part_cnt, stk_fsize(total_size), sorter_speed(ctx, total_size));
 
-  uns max_ways = 64;
+  uns max_ways = 1 << sorter_max_multiway_bits;
   struct sort_bucket *ways[max_ways+1];
   SORT_XTRACE(2, "Starting up to %d-way merge", max_ways);
   for (;;)
@@ -271,26 +271,6 @@ sorter_multiway(struct sort_context *ctx, struct sort_bucket *b)
     }
 }
 
-static uns
-sorter_radix_bits(struct sort_context *ctx, struct sort_bucket *b)
-{
-  if (!b->hash_bits || b->hash_bits < sorter_min_radix_bits ||
-      !ctx->radix_split ||
-      (b->flags & SBF_CUSTOM_PRESORT) ||
-      (sorter_debug & SORT_DEBUG_NO_RADIX))
-    return 0;
-
-  u64 in = sbuck_size(b);
-  u64 mem = ctx->internal_estimate(ctx, b) * 0.8;      // FIXME: Magical factor for hash non-uniformity
-  if (in <= mem)
-    return 0;
-
-  uns n = sorter_min_radix_bits;
-  while (n < sorter_max_radix_bits && n < b->hash_bits && (in >> n) > mem)
-    n++;
-  return n;
-}
-
 static void
 sorter_radix(struct sort_context *ctx, struct sort_bucket *b, uns bits)
 {
@@ -327,6 +307,80 @@ sorter_radix(struct sort_context *ctx, struct sort_bucket *b, uns bits)
   sbuck_drop(b);
 }
 
+static void
+sorter_decide(struct sort_context *ctx, struct sort_bucket *b)
+{
+  // Drop empty buckets
+  if (!sbuck_have(b))
+    {
+      SORT_XTRACE(3, "Dropping empty bucket");
+      sbuck_drop(b);
+      return;
+    }
+
+  // How many bits of bucket size we have to reduce before it fits in the RAM?
+  // (this is insanely large if the input size is unknown, but it serves our purpose)
+  u64 insize = sbuck_size(b);
+  u64 mem = ctx->internal_estimate(ctx, b) * 0.8;      // FIXME: Magical factor for various non-uniformities
+  uns bits = 0;
+  while ((insize >> bits) > mem)
+    bits++;
+
+  // Calculate the possibilities of radix splits
+  uns radix_bits;
+  if (!ctx->radix_split ||
+      (b->flags & SBF_CUSTOM_PRESORT) ||
+      (sorter_debug & SORT_DEBUG_NO_RADIX))
+    radix_bits = 0;
+  else
+    {
+      radix_bits = MIN(bits, b->hash_bits);
+      radix_bits = MIN(radix_bits, sorter_max_radix_bits);
+      if (radix_bits < sorter_min_radix_bits)
+       radix_bits = 0;
+    }
+
+  // The same for multi-way merges
+  uns multiway_bits;
+  if (!ctx->multiway_merge ||
+      (sorter_debug & SORT_DEBUG_NO_MULTIWAY) ||
+      (sorter_debug & SORT_DEBUG_NO_PRESORT))
+    multiway_bits = 0;
+  else
+    {
+      multiway_bits = MIN(bits, sorter_max_multiway_bits);
+      if (multiway_bits < sorter_min_multiway_bits)
+       multiway_bits = 0;
+    }
+
+  SORT_XTRACE(2, "Decisions: size=%s max=%s runs=%d bits=%d hash=%d -> radix=%d multi=%d",
+       stk_fsize(insize), stk_fsize(mem), b->runs, bits, b->hash_bits,
+       radix_bits, multiway_bits);
+
+  // If the input already consists of a single run, just join it
+  if (b->runs)
+    return sorter_join(b);
+
+  // If everything fits in memory, the 2-way strategy will sort it in memory
+  if (!bits)
+    return sorter_twoway(ctx, b);
+
+  // If we can reduce everything in one pass, do so and prefer radix splits
+  if (radix_bits == bits)
+    return sorter_radix(ctx, b, radix_bits);
+  if (multiway_bits == bits)
+    return sorter_multiway(ctx, b);
+
+  // Otherwise, reduce as much as possible and again prefer radix splits
+  if (radix_bits)
+    return sorter_radix(ctx, b, radix_bits);
+  if (multiway_bits)
+    return sorter_multiway(ctx, b);
+
+  // Fall back to 2-way strategy if nothing else applies
+  return sorter_twoway(ctx, b);
+}
+
 void
 sorter_run(struct sort_context *ctx)
 {
@@ -356,22 +410,10 @@ sorter_run(struct sort_context *ctx)
   bout->runs = 1;
   clist_add_head(&ctx->bucket_list, &bout->n);
 
+  // Repeatedly sort buckets
   struct sort_bucket *b;
-  uns bits;
   while (bout = clist_head(&ctx->bucket_list), b = clist_next(&ctx->bucket_list, &bout->n))
-    {
-      SORT_XTRACE(2, "Next block: %s, %d hash bits", F_BSIZE(b), b->hash_bits);
-      if (!sbuck_have(b))
-       sbuck_drop(b);
-      else if (b->runs == 1)
-       sorter_join(b);
-      else if (ctx->multiway_merge && !(sorter_debug & (SORT_DEBUG_NO_MULTIWAY | SORT_DEBUG_NO_PRESORT)))      // FIXME: Just kidding...
-       sorter_multiway(ctx, b);
-      else if (bits = sorter_radix_bits(ctx, b))
-       sorter_radix(ctx, b, bits);
-      else
-       sorter_twoway(ctx, b);
-    }
+    sorter_decide(ctx, b);
 
   sorter_free_buf(ctx);
   sbuck_write(bout);           // Force empty bucket to a file
index e506e3189161d905ca4a1408df789f799b4c86bc..bff52d6616ca9b0bfec6cd1908da4374984b4f71 100644 (file)
@@ -74,7 +74,7 @@ sbuck_swap_in(struct sort_bucket *b)
        bseek(b->fb, 0, SEEK_END);
       bconfig(b->fb, BCONFIG_IS_TEMP_FILE, 1);
       b->flags &= ~SBF_SWAPPED_OUT;
-      SORT_XTRACE(2, "Swapped in %s", b->filename);
+      SORT_XTRACE(3, "Swapped in %s", b->filename);
     }
 }
 
@@ -124,7 +124,7 @@ sbuck_swap_out(struct sort_bucket *b)
       bclose(b->fb);
       b->fb = NULL;
       b->flags |= SBF_SWAPPED_OUT;
-      SORT_XTRACE(2, "Swapped out %s", b->filename);
+      SORT_XTRACE(3, "Swapped out %s", b->filename);
     }
 }