From 94f78edcf06321189d48e9961bb4bc12e2e3c389 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 26 Mar 2026 22:53:32 +0100 Subject: [PATCH] performance-metrics: Add QCOW2 multi-cluster read micro benchmark Add micro_bench_qcow_multi_cluster_read which issues large reads spanning 8 contiguous clusters (512 KiB) per read_vectored call. This exercises the mapping coalesce path where multiple L2 entries are merged into fewer host I/O operations. Workloads: 128 and 256 total clusters (16 and 32 reads). Signed-off-by: Anatol Belski --- performance-metrics/src/main.rs | 26 ++++++++++++++++- performance-metrics/src/micro_bench_block.rs | 30 ++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/performance-metrics/src/main.rs b/performance-metrics/src/main.rs index d78e451b8..ffb42edfd 100644 --- a/performance-metrics/src/main.rs +++ b/performance-metrics/src/main.rs @@ -378,7 +378,7 @@ mod adjuster { } } -const TEST_LIST: [PerformanceTest; 78] = [ +const TEST_LIST: [PerformanceTest; 80] = [ PerformanceTest { name: "boot_time_ms", func_ptr: performance_boot_time, @@ -1445,6 +1445,30 @@ const TEST_LIST: [PerformanceTest; 78] = [ }, unit_adjuster: adjuster::s_to_us, }, + PerformanceTest { + name: "micro_block_qcow_multi_cluster_read_128_us", + func_ptr: micro_bench_block::micro_bench_qcow_multi_cluster_read, + control: PerformanceTestControl { + test_timeout: 10, + test_iterations: 20, + warmup_iterations: 5, + num_ops: Some(128), + ..PerformanceTestControl::default() + }, + unit_adjuster: adjuster::s_to_us, + }, + PerformanceTest { + name: "micro_block_qcow_multi_cluster_read_256_us", + func_ptr: micro_bench_block::micro_bench_qcow_multi_cluster_read, + control: PerformanceTestControl { + test_timeout: 10, + test_iterations: 20, + warmup_iterations: 5, + num_ops: Some(256), + ..PerformanceTestControl::default() + }, + unit_adjuster: adjuster::s_to_us, + }, ]; fn run_test_with_timeout( diff --git a/performance-metrics/src/micro_bench_block.rs b/performance-metrics/src/micro_bench_block.rs index 09cb09357..1b9ff3b8e 100644 --- a/performance-metrics/src/micro_bench_block.rs +++ b/performance-metrics/src/micro_bench_block.rs @@ -271,3 +271,33 @@ pub fn micro_bench_qcow_compressed_read(control: &PerformanceTestControl) -> f64 elapsed } + +/// Issue large multicluster reads from a prepopulated QCOW2 image. +/// +/// Each read_vectored call spans `CLUSTERS_PER_READ` contiguous clusters +/// (8 x 64 KiB = 512 KiB). This exercises the mapping coalesce path +/// where multiple L2 entries are merged into fewer host I/O operations. +/// `num_ops` is the total number of clusters; reads are issued in +/// chunks of CLUSTERS_PER_READ. +/// +/// Returns the total read wall clock time in seconds. +pub fn micro_bench_qcow_multi_cluster_read(control: &PerformanceTestControl) -> f64 { + const CLUSTERS_PER_READ: usize = 8; + + let num_ops = control.num_ops.expect("num_ops required") as usize; + let (_tmp, disk) = util::qcow_tempfile(num_ops); + let mut async_io = disk.new_async_io(1).expect("new_async_io failed"); + + let read_size = CLUSTERS_PER_READ * QCOW_CLUSTER_SIZE as usize; + let mut buf = vec![0u8; read_size]; + let iovec = read_iovec(&mut buf); + + let num_reads = num_ops / CLUSTERS_PER_READ; + let start = Instant::now(); + submit_reads(async_io.as_mut(), num_reads, read_size as u64, &[iovec]); + let elapsed = start.elapsed().as_secs_f64(); + + drain_completions(async_io.as_mut(), num_reads); + + elapsed +}