|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |