performance-metrics: Add qcow2 fsync micro benchmark

Add micro_bench_qcow_fsync which writes num_ops clusters into an
empty qcow2 image to dirty L2 and refcount metadata then times a
single fsync call that flushes all dirty tables to disk. This
isolates the metadata flush cost which scales with the number of
dirty L2 table entries and refcount blocks.

Two TEST_LIST entries: micro_block_qcow_fsync_64_us and
micro_block_qcow_fsync_256_us with 64 and 256 cluster workloads.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-03-15 14:17:55 +01:00
committed by Rob Bradford
parent c3312dca3f
commit e802c0d8b9
2 changed files with 55 additions and 1 deletions

View File

@@ -378,7 +378,7 @@ mod adjuster {
}
}
const TEST_LIST: [PerformanceTest; 68] = [
const TEST_LIST: [PerformanceTest; 70] = [
PerformanceTest {
name: "boot_time_ms",
func_ptr: performance_boot_time,
@@ -1325,6 +1325,30 @@ const TEST_LIST: [PerformanceTest; 68] = [
},
unit_adjuster: adjuster::s_to_us,
},
PerformanceTest {
name: "micro_block_qcow_fsync_128_us",
func_ptr: micro_bench_block::micro_bench_qcow_fsync,
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_fsync_256_us",
func_ptr: micro_bench_block::micro_bench_qcow_fsync,
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(

View File

@@ -132,3 +132,33 @@ pub fn micro_bench_qcow_punch_hole(control: &PerformanceTestControl) -> f64 {
elapsed
}
/// Write num_ops clusters into an empty qcow2 image to dirty L2 and
/// refcount metadata, then time a single fsync that flushes all dirty
/// tables to disk.
///
/// This isolates the metadata flush cost which scales with the number
/// of dirty L2 table entries and refcount blocks.
///
/// Returns the fsync wall clock time in seconds.
pub fn micro_bench_qcow_fsync(control: &PerformanceTestControl) -> f64 {
let num_ops = control.num_ops.expect("num_ops required") as usize;
let (_tmp, disk) = util::empty_qcow_tempfile(num_ops);
let mut async_io = disk.new_async_io(1).expect("new_async_io failed");
// Write num_ops clusters to dirty L2 and refcount metadata.
let buf = vec![0xA5u8; QCOW_CLUSTER_SIZE as usize];
let iovec = write_iovec(&buf);
submit_writes(async_io.as_mut(), num_ops, QCOW_CLUSTER_SIZE, &[iovec]);
// Drain write completions.
drain_completions(async_io.as_mut(), num_ops);
// Time the flush.
let start = Instant::now();
async_io.fsync(Some(num_ops as u64)).expect("fsync failed");
let elapsed = start.elapsed().as_secs_f64();
drain_completions(async_io.as_mut(), 1);
elapsed
}