[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v1 4/6] tools/libs/guest: adaptively disable LZ4 on poor compression ratio



If a VM's memory contain mostly incompressible page content, avoid
wasting CPU on both sender and receiver.

Add a per-iteration ratio tracker so that, after each precopy
iteration, the average compression ratio across all batches of that
iteration is computed; if it is worse than XC_SR_COMPRESS_ITER_THRESHOLD
(90%), ctx->compression is downgraded to XC_SR_COMPRESS_NONE for the
remainder of the migration.

Signed-off-by: Marcus Granado <marcus.granado@xxxxxxxxxx>
---
 tools/libs/guest/xg_sr_common.h |  18 ++++++
 tools/libs/guest/xg_sr_save.c   | 109 ++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/tools/libs/guest/xg_sr_common.h b/tools/libs/guest/xg_sr_common.h
index ccb02511ec..2b5b2ce26a 100644
--- a/tools/libs/guest/xg_sr_common.h
+++ b/tools/libs/guest/xg_sr_common.h
@@ -269,6 +269,24 @@ struct xc_sr_context
             unsigned long nr_deferred_pages;
             xc_hypercall_buffer_t dirty_bitmap_hbuf;
 
+            /*
+             * Compression ratio tracking per iteration. Accumulated by
+             * the compression function for each batch and evaluated at
+             * the end of every precopy iteration to decide whether to
+             * disable compression for the remaining iterations
+             */
+            uint64_t compress_raw_total;      /* uncompressed, this iter */
+            uint64_t compress_out_total;      /* compressed, this iter */
+
+            /*
+             * Cumulative compression totals across the whole save (not
+             * reset per iteration). Used for the final compression summary
+             * logged during teardown, remain valid even if adaptive
+             * disabling turned compression off partway through
+             */
+            uint64_t compress_raw_all_total;  /* uncompressed, all iters */
+            uint64_t compress_out_all_total;  /* compressed, all iters */
+
 #ifdef HAVE_LZ4
             /*
              * Persistent LZ4 output buffer (avoids repeated malloc/free),
diff --git a/tools/libs/guest/xg_sr_save.c b/tools/libs/guest/xg_sr_save.c
index 8f661f0168..dbbcddf6fd 100644
--- a/tools/libs/guest/xg_sr_save.c
+++ b/tools/libs/guest/xg_sr_save.c
@@ -19,6 +19,15 @@
  */
 #define XC_SR_COMPRESS_BATCH_THRESHOLD  97
 
+/*
+ * Per-iteration compression ratio threshold (percentage). After each
+ * precopy iteration, the average compression ratio across all batches is
+ * calculated. If it exceeds this value, compression is disabled for all
+ * remaining iterations, as the data is not compressible enough to
+ * justify the CPU cost. Set to >= 100 to disable this adaptive behaviour
+ */
+#define XC_SR_COMPRESS_ITER_THRESHOLD   90
+
 /*
  * Writes an Image header and Domain header into the stream.
  */
@@ -89,6 +98,99 @@ static int write_checkpoint_record(struct xc_sr_context *ctx)
     return write_record(ctx, &checkpoint);
 }
 
+/*
+ * Reset accumulators for compression ratio per iteration. Called at the
+ * start of each precopy iteration so that the ratio reflects only the
+ * current iteration's batches
+ */
+static void compress_stats_reset(struct xc_sr_context *ctx)
+{
+    ctx->save.compress_raw_total = 0;
+    ctx->save.compress_out_total = 0;
+}
+
+/*
+ * Accumulate compression statistics for a single batch. Called from
+ * the compression function after every LZ4_compress_default() (or any
+ * future compressor) so the totals per iteration stay up to date
+ */
+static inline void compress_stats_accumulate(struct xc_sr_context *ctx,
+                                             size_t raw_len,
+                                             size_t compressed_len)
+{
+    ctx->save.compress_raw_total += raw_len;
+    ctx->save.compress_out_total += compressed_len;
+    ctx->save.compress_raw_all_total += raw_len;
+    ctx->save.compress_out_all_total += compressed_len;
+}
+
+static void cleanup_compression(struct xc_sr_context *ctx);
+
+/*
+ * Evaluate the compression ratio per iteration. If the average ratio
+ * across all batches in the completed iteration exceeds the threshold,
+ * the data is not compressible enough to justify the CPU cost, then
+ * disable compression for all remaining iterations
+ */
+static void compress_stats_evaluate(struct xc_sr_context *ctx,
+                                    unsigned int iteration)
+{
+    xc_interface *xch = ctx->xch;
+    unsigned long ratio_permil;
+
+    if ( ctx->compression == XC_SR_COMPRESS_NONE )
+        return;
+
+    if ( ctx->save.compress_raw_total == 0 )
+        return;
+
+    ratio_permil = (unsigned long)(ctx->save.compress_out_total * 1000 /
+                                   ctx->save.compress_raw_total);
+
+    IPRINTF("Iteration %u: compressed %lu -> %lu bytes "
+            "(%lu.%lu%% average ratio)",
+            iteration,
+            (unsigned long)ctx->save.compress_raw_total,
+            (unsigned long)ctx->save.compress_out_total,
+            ratio_permil / 10, ratio_permil % 10);
+
+    if ( ctx->save.compress_out_total * 100 >=
+         ctx->save.compress_raw_total * XC_SR_COMPRESS_ITER_THRESHOLD )
+    {
+        IPRINTF("Iteration %u: average compression ratio %lu.%lu%% "
+                "(>= %u%% threshold) - disabling compression for "
+                "remaining iterations",
+                iteration, ratio_permil / 10, ratio_permil % 10,
+                XC_SR_COMPRESS_ITER_THRESHOLD);
+        cleanup_compression(ctx);
+        ctx->compression = XC_SR_COMPRESS_NONE;
+    }
+}
+
+/*
+ * Log a final compression summary covering the whole save. Called once
+ * during teardown. compress_*_all_total accumulate across all iterations
+ * (never reset), so this is meaningful even if adaptive disabling turned
+ * compression off partway through
+ */
+static void compress_stats_summary(struct xc_sr_context *ctx)
+{
+    xc_interface *xch = ctx->xch;
+    unsigned long ratio_permil;
+
+    if ( ctx->save.compress_raw_all_total == 0 )
+        return;
+
+    ratio_permil = (unsigned long)(ctx->save.compress_out_all_total * 1000 /
+                                   ctx->save.compress_raw_all_total);
+
+    IPRINTF("Compression summary: compressed %lu -> %lu bytes "
+            "(%lu.%lu%% average ratio) over the whole save",
+            (unsigned long)ctx->save.compress_raw_all_total,
+            (unsigned long)ctx->save.compress_out_all_total,
+            ratio_permil / 10, ratio_permil % 10);
+}
+
 #ifdef HAVE_LZ4
 /*
  * Allocate LZ4 compression buffers during setup_compression()
@@ -196,6 +298,8 @@ static bool lz4_compress_batch(struct xc_sr_context *ctx, 
unsigned int nr_pages,
         compressed_len = off;
     }
 
+    compress_stats_accumulate(ctx, raw_len, compressed_len);
+
     /*
      * Decline: if compression didn't shrink the data enough, send
      * uncompressed page data instead. This avoids transmitting extra
@@ -702,6 +806,8 @@ static int send_memory_live(struct xc_sr_context *ctx)
         policy_decision = precopy_policy(*policy_stats, data);
         x++;
 
+        compress_stats_reset(ctx);
+
         if ( stats.dirty_count > 0 && policy_decision != XGS_POLICY_ABORT )
         {
             rc = update_progress_string(ctx, &progress_str);
@@ -713,6 +819,8 @@ static int send_memory_live(struct xc_sr_context *ctx)
                 goto out;
         }
 
+        compress_stats_evaluate(ctx, x);
+
         if ( policy_decision != XGS_POLICY_CONTINUE_PRECOPY )
             break;
 
@@ -1055,6 +1163,7 @@ static void cleanup(struct xc_sr_context *ctx)
     free(ctx->save.deferred_pages);
     free(ctx->save.batch_pfns);
 
+    compress_stats_summary(ctx);
     cleanup_compression(ctx);
 }
 
-- 
2.43.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.