From e9d1ffd24f0112477dd35a137316d746d5a920f8 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 27 Mar 2026 09:30:13 +0100 Subject: [PATCH] performance-metrics: Add QCOW2 batch read micro benchmark Add micro_bench_qcow_batch_read which builds a batch of num_ops read requests and submits them all at once through submit_batch_requests. This exercises the io_uring batch submission path added in qcow_async, where multiple SQEs are packed into a single io_uring_enter call. Signed-off-by: Anatol Belski --- performance-metrics/src/main.rs | 26 +++++++++++- performance-metrics/src/micro_bench_block.rs | 42 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/performance-metrics/src/main.rs b/performance-metrics/src/main.rs index 1177b54f4..f68471fd6 100644 --- a/performance-metrics/src/main.rs +++ b/performance-metrics/src/main.rs @@ -378,7 +378,7 @@ mod adjuster { } } -const TEST_LIST: [PerformanceTest; 84] = [ +const TEST_LIST: [PerformanceTest; 86] = [ PerformanceTest { name: "boot_time_ms", func_ptr: performance_boot_time, @@ -1517,6 +1517,30 @@ const TEST_LIST: [PerformanceTest; 84] = [ }, unit_adjuster: adjuster::s_to_us, }, + PerformanceTest { + name: "micro_block_qcow_batch_read_128_us", + func_ptr: micro_bench_block::micro_bench_qcow_batch_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_batch_read_256_us", + func_ptr: micro_bench_block::micro_bench_qcow_batch_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 5ab907a87..5f2eabc28 100644 --- a/performance-metrics/src/micro_bench_block.rs +++ b/performance-metrics/src/micro_bench_block.rs @@ -13,6 +13,7 @@ use std::time::Instant; use block::async_io::AsyncIo; use block::disk_file::AsyncDiskFile; use block::raw_async_aio::RawFileAsyncAio; +use block::{BatchRequest, RequestType}; use crate::PerformanceTestControl; use crate::util::{ @@ -355,3 +356,44 @@ pub fn micro_bench_qcow_async_read(control: &PerformanceTestControl) -> f64 { drain_async_completions(async_io.as_mut(), num_ops); start.elapsed().as_secs_f64() } + +/// Measure QCOW2 batch read submission via io_uring. +/// +/// Builds a batch of `num_ops` read requests and submits them all at once +/// through `submit_batch_requests`, which packs multiple SQEs into a single +/// io_uring submission. Returns the total wall clock time in seconds. +pub fn micro_bench_qcow_batch_read(control: &PerformanceTestControl) -> f64 { + let num_ops = control.num_ops.expect("num_ops required") as usize; + let (_tmp, disk) = util::qcow_async_tempfile(num_ops); + let mut async_io = disk + .new_async_io(num_ops as u32) + .expect("new_async_io failed"); + + let mut buf = vec![0u8; num_ops * QCOW_CLUSTER_SIZE as usize]; + + let batch: Vec = (0..num_ops) + .map(|i| { + let slice = + &mut buf[i * QCOW_CLUSTER_SIZE as usize..(i + 1) * QCOW_CLUSTER_SIZE as usize]; + BatchRequest { + offset: (i as u64 * QCOW_CLUSTER_SIZE) as libc::off_t, + iovecs: vec![libc::iovec { + iov_base: slice.as_mut_ptr() as *mut libc::c_void, + iov_len: QCOW_CLUSTER_SIZE as usize, + }] + .into(), + user_data: i as u64, + request_type: RequestType::In, + } + }) + .collect(); + + let start = Instant::now(); + async_io + .submit_batch_requests(&batch) + .expect("submit_batch_requests failed"); + + // Drain all io_uring completions before stopping the clock. + drain_async_completions(async_io.as_mut(), num_ops); + start.elapsed().as_secs_f64() +}