mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
performance-metrics: Add AIO completion drain micro benchmark
Add micro_block_raw_aio_drain_128_us and micro_block_raw_aio_drain_256_us tests that submit N AIO writes to a temporary file, wait for the eventfd signal, then time how long it takes to drain all completions via next_completed_request(). This measures per completion syscall overhead and provides a baseline before any batching optimizations. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1596,8 +1596,10 @@ dependencies = [
|
|||||||
name = "performance-metrics"
|
name = "performance-metrics"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"block",
|
||||||
"clap",
|
"clap",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"libc",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"test_infra",
|
"test_infra",
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ name = "performance-metrics"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
block = { path = "../block" }
|
||||||
clap = { workspace = true, features = ["wrap_help"] }
|
clap = { workspace = true, features = ["wrap_help"] }
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
|
libc = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive", "rc"] }
|
serde = { workspace = true, features = ["derive", "rc"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
test_infra = { path = "../test_infra" }
|
test_infra = { path = "../test_infra" }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Custom harness to run performance tests
|
// Custom harness to run performance tests
|
||||||
|
mod micro_bench_block;
|
||||||
mod performance_tests;
|
mod performance_tests;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
@@ -336,6 +337,10 @@ mod adjuster {
|
|||||||
v * 1000.0
|
v * 1000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn s_to_us(v: f64) -> f64 {
|
||||||
|
v * 1_000_000.0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bps_to_gbps(v: f64) -> f64 {
|
pub fn bps_to_gbps(v: f64) -> f64 {
|
||||||
v / (1_000_000_000_f64)
|
v / (1_000_000_000_f64)
|
||||||
}
|
}
|
||||||
@@ -346,7 +351,7 @@ mod adjuster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEST_LIST: [PerformanceTest; 60] = [
|
const TEST_LIST: [PerformanceTest; 62] = [
|
||||||
PerformanceTest {
|
PerformanceTest {
|
||||||
name: "boot_time_ms",
|
name: "boot_time_ms",
|
||||||
func_ptr: performance_boot_time,
|
func_ptr: performance_boot_time,
|
||||||
@@ -1197,6 +1202,30 @@ const TEST_LIST: [PerformanceTest; 60] = [
|
|||||||
},
|
},
|
||||||
unit_adjuster: adjuster::Bps_to_MiBps,
|
unit_adjuster: adjuster::Bps_to_MiBps,
|
||||||
},
|
},
|
||||||
|
PerformanceTest {
|
||||||
|
name: "micro_block_raw_aio_drain_128_us",
|
||||||
|
func_ptr: micro_bench_block::micro_bench_aio_drain,
|
||||||
|
control: PerformanceTestControl {
|
||||||
|
test_timeout: 5,
|
||||||
|
test_iterations: 20,
|
||||||
|
warmup_iterations: 5,
|
||||||
|
num_ops: Some(128),
|
||||||
|
..PerformanceTestControl::default()
|
||||||
|
},
|
||||||
|
unit_adjuster: adjuster::s_to_us,
|
||||||
|
},
|
||||||
|
PerformanceTest {
|
||||||
|
name: "micro_block_raw_aio_drain_256_us",
|
||||||
|
func_ptr: micro_bench_block::micro_bench_aio_drain,
|
||||||
|
control: PerformanceTestControl {
|
||||||
|
test_timeout: 5,
|
||||||
|
test_iterations: 20,
|
||||||
|
warmup_iterations: 5,
|
||||||
|
num_ops: Some(256),
|
||||||
|
..PerformanceTestControl::default()
|
||||||
|
},
|
||||||
|
unit_adjuster: adjuster::s_to_us,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
fn run_test_with_timeout(
|
fn run_test_with_timeout(
|
||||||
|
|||||||
53
performance-metrics/src/micro_bench_block.rs
Normal file
53
performance-metrics/src/micro_bench_block.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//! In process micro benchmarks for block layer internals.
|
||||||
|
//!
|
||||||
|
//! These run without booting a VM and measure hot path operations
|
||||||
|
//! (e.g. AIO completion draining) at the syscall level.
|
||||||
|
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use block::async_io::AsyncIo;
|
||||||
|
use block::raw_async_aio::RawFileAsyncAio;
|
||||||
|
|
||||||
|
use crate::PerformanceTestControl;
|
||||||
|
use crate::util::{self, BLOCK_SIZE};
|
||||||
|
|
||||||
|
/// Submit num_ops AIO writes, wait for them all to land, then time
|
||||||
|
/// how long it takes to drain every completion via next_completed_request().
|
||||||
|
///
|
||||||
|
/// Returns the drain wall clock time in seconds.
|
||||||
|
pub fn micro_bench_aio_drain(control: &PerformanceTestControl) -> f64 {
|
||||||
|
let num_ops = control.num_ops.expect("num_ops required") as usize;
|
||||||
|
let tmp = util::sized_tempfile(num_ops);
|
||||||
|
let fd = tmp.as_file().as_raw_fd();
|
||||||
|
let mut aio = RawFileAsyncAio::new(fd, num_ops as u32).expect("failed to create AIO context");
|
||||||
|
|
||||||
|
let buf = vec![0xA5u8; BLOCK_SIZE as usize];
|
||||||
|
|
||||||
|
// Submit all writes.
|
||||||
|
for i in 0..num_ops {
|
||||||
|
let iovec = libc::iovec {
|
||||||
|
iov_base: buf.as_ptr() as *mut _,
|
||||||
|
iov_len: buf.len(),
|
||||||
|
};
|
||||||
|
aio.write_vectored((i as u64 * BLOCK_SIZE) as libc::off_t, &[iovec], i as u64)
|
||||||
|
.expect("write_vectored failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the eventfd signals that completions are available.
|
||||||
|
util::wait_for_eventfd(aio.notifier());
|
||||||
|
|
||||||
|
// Drain all completions and measure.
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut drained = 0usize;
|
||||||
|
while drained < num_ops {
|
||||||
|
if aio.next_completed_request().is_some() {
|
||||||
|
drained += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start.elapsed().as_secs_f64()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user