xref: /cloud-hypervisor/performance-metrics/src/main.rs (revision 61e57e1cb149de03ae1e0b799b9e5ba9a4a63ace)
1128d0984SRob Bradford // Copyright © 2022 Intel Corporation
2128d0984SRob Bradford //
3128d0984SRob Bradford // SPDX-License-Identifier: Apache-2.0
4128d0984SRob Bradford //
5128d0984SRob Bradford 
61a17564eSBo Chen // Custom harness to run performance tests
71a17564eSBo Chen extern crate test_infra;
81a17564eSBo Chen 
91a17564eSBo Chen mod performance_tests;
101a17564eSBo Chen 
11*61e57e1cSRuoqing He use std::process::Command;
12*61e57e1cSRuoqing He use std::sync::mpsc::channel;
13*61e57e1cSRuoqing He use std::sync::Arc;
14*61e57e1cSRuoqing He use std::time::Duration;
15*61e57e1cSRuoqing He use std::{env, fmt, thread};
1688a9f799SRob Bradford 
1788a9f799SRob Bradford use clap::{Arg, ArgAction, Command as ClapCommand};
1888a9f799SRob Bradford use performance_tests::*;
1988a9f799SRob Bradford use serde::{Deserialize, Serialize};
20d8a67259SBo Chen use test_infra::FioOps;
21d650c684SBo Chen use thiserror::Error;
226b915de2SBo Chen 
23d650c684SBo Chen #[derive(Error, Debug)]
246b915de2SBo Chen enum Error {
25d650c684SBo Chen     #[error("Error: test timed-out")]
266b915de2SBo Chen     TestTimeout,
27d650c684SBo Chen     #[error("Error: test failed")]
286b915de2SBo Chen     TestFailed,
296b915de2SBo Chen }
306b915de2SBo Chen 
316b915de2SBo Chen #[derive(Deserialize, Serialize)]
326b915de2SBo Chen pub struct PerformanceTestResult {
336b915de2SBo Chen     name: String,
346b915de2SBo Chen     mean: f64,
356b915de2SBo Chen     std_dev: f64,
36e41fe0acSBo Chen     max: f64,
37e41fe0acSBo Chen     min: f64,
386b915de2SBo Chen }
391a17564eSBo Chen 
400862064fSBo Chen #[derive(Deserialize, Serialize)]
410862064fSBo Chen pub struct MetricsReport {
420862064fSBo Chen     pub git_human_readable: String,
430862064fSBo Chen     pub git_revision: String,
4484586960SRob Bradford     pub git_commit_date: String,
450862064fSBo Chen     pub date: String,
460862064fSBo Chen     pub results: Vec<PerformanceTestResult>,
470862064fSBo Chen }
480862064fSBo Chen 
495bd305faSBo Chen impl Default for MetricsReport {
default() -> Self505bd305faSBo Chen     fn default() -> Self {
515bd305faSBo Chen         let mut git_human_readable = "".to_string();
52f32487f8SRob Bradford         if let Ok(git_out) = Command::new("git").args(["describe", "--dirty"]).output() {
535bd305faSBo Chen             if git_out.status.success() {
54cac42301SRob Bradford                 git_human_readable = String::from_utf8(git_out.stdout)
55cac42301SRob Bradford                     .unwrap()
56cac42301SRob Bradford                     .trim()
57cac42301SRob Bradford                     .to_string();
58cac42301SRob Bradford             } else {
59cac42301SRob Bradford                 eprintln!(
60cac42301SRob Bradford                     "Error generating human readable git reference: {}",
61cac42301SRob Bradford                     String::from_utf8(git_out.stderr).unwrap()
62cac42301SRob Bradford                 );
635bd305faSBo Chen             }
645bd305faSBo Chen         }
655bd305faSBo Chen 
665bd305faSBo Chen         let mut git_revision = "".to_string();
67f32487f8SRob Bradford         if let Ok(git_out) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
685bd305faSBo Chen             if git_out.status.success() {
69cac42301SRob Bradford                 git_revision = String::from_utf8(git_out.stdout)
70cac42301SRob Bradford                     .unwrap()
71cac42301SRob Bradford                     .trim()
72cac42301SRob Bradford                     .to_string();
73cac42301SRob Bradford             } else {
74cac42301SRob Bradford                 eprintln!(
75cac42301SRob Bradford                     "Error generating git reference: {}",
76cac42301SRob Bradford                     String::from_utf8(git_out.stderr).unwrap()
77cac42301SRob Bradford                 );
785bd305faSBo Chen             }
795bd305faSBo Chen         }
805bd305faSBo Chen 
8184586960SRob Bradford         let mut git_commit_date = "".to_string();
825bd305faSBo Chen         if let Ok(git_out) = Command::new("git")
83f32487f8SRob Bradford             .args(["show", "-s", "--format=%cd"])
845bd305faSBo Chen             .output()
855bd305faSBo Chen         {
865bd305faSBo Chen             if git_out.status.success() {
87cac42301SRob Bradford                 git_commit_date = String::from_utf8(git_out.stdout)
88cac42301SRob Bradford                     .unwrap()
89cac42301SRob Bradford                     .trim()
90cac42301SRob Bradford                     .to_string();
91cac42301SRob Bradford             } else {
92cac42301SRob Bradford                 eprintln!(
93cac42301SRob Bradford                     "Error generating git commit date: {}",
94cac42301SRob Bradford                     String::from_utf8(git_out.stderr).unwrap()
95cac42301SRob Bradford                 );
965bd305faSBo Chen             }
975bd305faSBo Chen         }
985bd305faSBo Chen 
995bd305faSBo Chen         MetricsReport {
1005bd305faSBo Chen             git_human_readable,
1015bd305faSBo Chen             git_revision,
10284586960SRob Bradford             git_commit_date,
1035bd305faSBo Chen             date: date(),
1045bd305faSBo Chen             results: Vec::new(),
1055bd305faSBo Chen         }
1065bd305faSBo Chen     }
1075bd305faSBo Chen }
1085bd305faSBo Chen 
109025447e8SRob Bradford #[derive(Default)]
110025447e8SRob Bradford pub struct PerformanceTestOverrides {
111025447e8SRob Bradford     test_iterations: Option<u32>,
1127e47464aSsmit-gardhariya     test_timeout: Option<u32>,
113025447e8SRob Bradford }
114025447e8SRob Bradford 
115025447e8SRob Bradford impl fmt::Display for PerformanceTestOverrides {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result116025447e8SRob Bradford     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117025447e8SRob Bradford         if let Some(test_iterations) = self.test_iterations {
1187e47464aSsmit-gardhariya             write!(f, "test_iterations = {test_iterations}, ")?;
1197e47464aSsmit-gardhariya         }
1207e47464aSsmit-gardhariya         if let Some(test_timeout) = self.test_timeout {
1217e47464aSsmit-gardhariya             write!(f, "test_timeout = {test_timeout}")?;
122025447e8SRob Bradford         }
123025447e8SRob Bradford 
124025447e8SRob Bradford         Ok(())
125025447e8SRob Bradford     }
126025447e8SRob Bradford }
127025447e8SRob Bradford 
1287e47464aSsmit-gardhariya #[derive(Clone)]
1291a17564eSBo Chen pub struct PerformanceTestControl {
13060d80577SRob Bradford     test_timeout: u32,
1311a17564eSBo Chen     test_iterations: u32,
1325e3d7267SRob Bradford     num_queues: Option<u32>,
1331a17564eSBo Chen     queue_size: Option<u32>,
13403d8a3ebSWei Liu     net_control: Option<(bool, bool)>, // First bool is for RX(true)/TX(false), second bool is for bandwidth or PPS
135e16817eaSWei Liu     fio_control: Option<(FioOps, bool)>, // Second parameter controls whether we want bandwidth or IOPS
1363c0817a1SRob Bradford     num_boot_vcpus: Option<u8>,
1371a17564eSBo Chen }
1381a17564eSBo Chen 
1396b915de2SBo Chen impl fmt::Display for PerformanceTestControl {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1406b915de2SBo Chen     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1416b915de2SBo Chen         let mut output = format!(
142acafda67SRob Bradford             "test_timeout = {}s, test_iterations = {}",
14360d80577SRob Bradford             self.test_timeout, self.test_iterations
1446b915de2SBo Chen         );
1455e3d7267SRob Bradford         if let Some(o) = self.num_queues {
1465e527294SRob Bradford             output = format!("{output}, num_queues = {o}");
1476b915de2SBo Chen         }
1486b915de2SBo Chen         if let Some(o) = self.queue_size {
1495e527294SRob Bradford             output = format!("{output}, queue_size = {o}");
1506b915de2SBo Chen         }
15103d8a3ebSWei Liu         if let Some(o) = self.net_control {
15203d8a3ebSWei Liu             let (rx, bw) = o;
15303d8a3ebSWei Liu             output = format!("{output}, rx = {rx}, bandwidth = {bw}");
1546b915de2SBo Chen         }
155e16817eaSWei Liu         if let Some(o) = &self.fio_control {
156e16817eaSWei Liu             let (ops, bw) = o;
157e16817eaSWei Liu             output = format!("{output}, fio_ops = {ops}, bandwidth = {bw}");
1586b915de2SBo Chen         }
1596b915de2SBo Chen 
1605e527294SRob Bradford         write!(f, "{output}")
1616b915de2SBo Chen     }
1626b915de2SBo Chen }
1636b915de2SBo Chen 
164ec7d4e72SBo Chen impl PerformanceTestControl {
default() -> Self165ec7d4e72SBo Chen     const fn default() -> Self {
1661a17564eSBo Chen         Self {
16760d80577SRob Bradford             test_timeout: 10,
1683f4cbce9SRob Bradford             test_iterations: 5,
1695e3d7267SRob Bradford             num_queues: None,
170ec7d4e72SBo Chen             queue_size: None,
17103d8a3ebSWei Liu             net_control: None,
172e16817eaSWei Liu             fio_control: None,
1733c0817a1SRob Bradford             num_boot_vcpus: Some(1),
1741a17564eSBo Chen         }
1751a17564eSBo Chen     }
1761a17564eSBo Chen }
1771a17564eSBo Chen 
1781a17564eSBo Chen /// A performance test should finish within the a certain time-out and
1791a17564eSBo Chen /// return a performance metrics number (including the average number and
1801a17564eSBo Chen /// standard deviation)
1811a17564eSBo Chen struct PerformanceTest {
1821a17564eSBo Chen     pub name: &'static str,
1831a17564eSBo Chen     pub func_ptr: fn(&PerformanceTestControl) -> f64,
1841a17564eSBo Chen     pub control: PerformanceTestControl,
185638c4a57SRob Bradford     unit_adjuster: fn(f64) -> f64,
1861a17564eSBo Chen }
1871a17564eSBo Chen 
1881a17564eSBo Chen impl PerformanceTest {
run(&self, overrides: &PerformanceTestOverrides) -> PerformanceTestResult189025447e8SRob Bradford     pub fn run(&self, overrides: &PerformanceTestOverrides) -> PerformanceTestResult {
1901a17564eSBo Chen         let mut metrics = Vec::new();
191025447e8SRob Bradford         for _ in 0..overrides
192025447e8SRob Bradford             .test_iterations
193025447e8SRob Bradford             .unwrap_or(self.control.test_iterations)
194025447e8SRob Bradford         {
1957e47464aSsmit-gardhariya             // update the timeout in control if passed explicitly and run testcase with it
1967e47464aSsmit-gardhariya             if let Some(test_timeout) = overrides.test_timeout {
1977e47464aSsmit-gardhariya                 let mut control: PerformanceTestControl = self.control.clone();
1987e47464aSsmit-gardhariya                 control.test_timeout = test_timeout;
1997e47464aSsmit-gardhariya                 metrics.push((self.func_ptr)(&control));
2007e47464aSsmit-gardhariya             } else {
2011a17564eSBo Chen                 metrics.push((self.func_ptr)(&self.control));
2021a17564eSBo Chen             }
2037e47464aSsmit-gardhariya         }
2041a17564eSBo Chen 
205638c4a57SRob Bradford         let mean = (self.unit_adjuster)(mean(&metrics).unwrap());
206638c4a57SRob Bradford         let std_dev = (self.unit_adjuster)(std_deviation(&metrics).unwrap());
207638c4a57SRob Bradford         let max = (self.unit_adjuster)(metrics.clone().into_iter().reduce(f64::max).unwrap());
208638c4a57SRob Bradford         let min = (self.unit_adjuster)(metrics.clone().into_iter().reduce(f64::min).unwrap());
2091a17564eSBo Chen 
2106b915de2SBo Chen         PerformanceTestResult {
2116b915de2SBo Chen             name: self.name.to_string(),
2126b915de2SBo Chen             mean,
2136b915de2SBo Chen             std_dev,
214e41fe0acSBo Chen             max,
215e41fe0acSBo Chen             min,
2166b915de2SBo Chen         }
2176b915de2SBo Chen     }
2181a17564eSBo Chen 
2196b915de2SBo Chen     // Calculate the timeout for each test
2206b915de2SBo Chen     // Note: To cover the setup/cleanup time, 20s is added for each iteration of the test
calc_timeout(&self, test_iterations: &Option<u32>, test_timeout: &Option<u32>) -> u642217e47464aSsmit-gardhariya     pub fn calc_timeout(&self, test_iterations: &Option<u32>, test_timeout: &Option<u32>) -> u64 {
2227e47464aSsmit-gardhariya         ((test_timeout.unwrap_or(self.control.test_timeout) + 20)
2237e47464aSsmit-gardhariya             * test_iterations.unwrap_or(self.control.test_iterations)) as u64
2241a17564eSBo Chen     }
2251a17564eSBo Chen }
2261a17564eSBo Chen 
mean(data: &[f64]) -> Option<f64>2271a17564eSBo Chen fn mean(data: &[f64]) -> Option<f64> {
2281a17564eSBo Chen     let count = data.len();
2291a17564eSBo Chen 
2301a17564eSBo Chen     if count > 0 {
2311a17564eSBo Chen         Some(data.iter().sum::<f64>() / count as f64)
2321a17564eSBo Chen     } else {
2331a17564eSBo Chen         None
2341a17564eSBo Chen     }
2351a17564eSBo Chen }
2361a17564eSBo Chen 
std_deviation(data: &[f64]) -> Option<f64>2371a17564eSBo Chen fn std_deviation(data: &[f64]) -> Option<f64> {
2381a17564eSBo Chen     let count = data.len();
2391a17564eSBo Chen 
2401a17564eSBo Chen     if count > 0 {
2411a17564eSBo Chen         let mean = mean(data).unwrap();
2421a17564eSBo Chen         let variance = data
2431a17564eSBo Chen             .iter()
2441a17564eSBo Chen             .map(|value| {
2451a17564eSBo Chen                 let diff = mean - *value;
2461a17564eSBo Chen                 diff * diff
2471a17564eSBo Chen             })
2481a17564eSBo Chen             .sum::<f64>()
2491a17564eSBo Chen             / count as f64;
2501a17564eSBo Chen 
2511a17564eSBo Chen         Some(variance.sqrt())
2521a17564eSBo Chen     } else {
2531a17564eSBo Chen         None
2541a17564eSBo Chen     }
2551a17564eSBo Chen }
2561a17564eSBo Chen 
257638c4a57SRob Bradford mod adjuster {
identity(v: f64) -> f64258638c4a57SRob Bradford     pub fn identity(v: f64) -> f64 {
259638c4a57SRob Bradford         v
260638c4a57SRob Bradford     }
261638c4a57SRob Bradford 
s_to_ms(v: f64) -> f64262638c4a57SRob Bradford     pub fn s_to_ms(v: f64) -> f64 {
263638c4a57SRob Bradford         v * 1000.0
264638c4a57SRob Bradford     }
265638c4a57SRob Bradford 
bps_to_gbps(v: f64) -> f64266638c4a57SRob Bradford     pub fn bps_to_gbps(v: f64) -> f64 {
267638c4a57SRob Bradford         v / (1_000_000_000_f64)
268638c4a57SRob Bradford     }
269638c4a57SRob Bradford 
270638c4a57SRob Bradford     #[allow(non_snake_case)]
Bps_to_MiBps(v: f64) -> f64271638c4a57SRob Bradford     pub fn Bps_to_MiBps(v: f64) -> f64 {
272638c4a57SRob Bradford         v / (1 << 20) as f64
273638c4a57SRob Bradford     }
274638c4a57SRob Bradford }
275638c4a57SRob Bradford 
276e18d32baSSongqian Li const TEST_LIST: [PerformanceTest; 30] = [
277ec7d4e72SBo Chen     PerformanceTest {
278638c4a57SRob Bradford         name: "boot_time_ms",
2791a17564eSBo Chen         func_ptr: performance_boot_time,
2801a17564eSBo Chen         control: PerformanceTestControl {
28160d80577SRob Bradford             test_timeout: 2,
2821a17564eSBo Chen             test_iterations: 10,
283ec7d4e72SBo Chen             ..PerformanceTestControl::default()
284ec7d4e72SBo Chen         },
285638c4a57SRob Bradford         unit_adjuster: adjuster::s_to_ms,
286ec7d4e72SBo Chen     },
287ec7d4e72SBo Chen     PerformanceTest {
288638c4a57SRob Bradford         name: "boot_time_pmem_ms",
2891a17564eSBo Chen         func_ptr: performance_boot_time_pmem,
2901a17564eSBo Chen         control: PerformanceTestControl {
29160d80577SRob Bradford             test_timeout: 2,
2921a17564eSBo Chen             test_iterations: 10,
293ec7d4e72SBo Chen             ..PerformanceTestControl::default()
294ec7d4e72SBo Chen         },
295638c4a57SRob Bradford         unit_adjuster: adjuster::s_to_ms,
296ec7d4e72SBo Chen     },
297ec7d4e72SBo Chen     PerformanceTest {
2983c0817a1SRob Bradford         name: "boot_time_16_vcpus_ms",
2993c0817a1SRob Bradford         func_ptr: performance_boot_time,
3003c0817a1SRob Bradford         control: PerformanceTestControl {
3013c0817a1SRob Bradford             test_timeout: 2,
3023c0817a1SRob Bradford             test_iterations: 10,
3033c0817a1SRob Bradford             num_boot_vcpus: Some(16),
3043c0817a1SRob Bradford             ..PerformanceTestControl::default()
3053c0817a1SRob Bradford         },
3063c0817a1SRob Bradford         unit_adjuster: adjuster::s_to_ms,
3073c0817a1SRob Bradford     },
3083c0817a1SRob Bradford     PerformanceTest {
309e18d32baSSongqian Li         name: "restore_latency_time_ms",
310e18d32baSSongqian Li         func_ptr: performance_restore_latency,
311e18d32baSSongqian Li         control: PerformanceTestControl {
312e18d32baSSongqian Li             test_timeout: 2,
313e18d32baSSongqian Li             test_iterations: 10,
314e18d32baSSongqian Li             ..PerformanceTestControl::default()
315e18d32baSSongqian Li         },
316e18d32baSSongqian Li         unit_adjuster: adjuster::identity,
317e18d32baSSongqian Li     },
318e18d32baSSongqian Li     PerformanceTest {
3193c0817a1SRob Bradford         name: "boot_time_16_vcpus_pmem_ms",
3203c0817a1SRob Bradford         func_ptr: performance_boot_time_pmem,
3213c0817a1SRob Bradford         control: PerformanceTestControl {
3223c0817a1SRob Bradford             test_timeout: 2,
3233c0817a1SRob Bradford             test_iterations: 10,
3243c0817a1SRob Bradford             num_boot_vcpus: Some(16),
3253c0817a1SRob Bradford             ..PerformanceTestControl::default()
3263c0817a1SRob Bradford         },
3273c0817a1SRob Bradford         unit_adjuster: adjuster::s_to_ms,
3283c0817a1SRob Bradford     },
3293c0817a1SRob Bradford     PerformanceTest {
3309592accbSRob Bradford         name: "virtio_net_latency_us",
3311a17564eSBo Chen         func_ptr: performance_net_latency,
332f254f11bSRob Bradford         control: PerformanceTestControl {
333f254f11bSRob Bradford             num_queues: Some(2),
334f254f11bSRob Bradford             queue_size: Some(256),
335f254f11bSRob Bradford             ..PerformanceTestControl::default()
336f254f11bSRob Bradford         },
337638c4a57SRob Bradford         unit_adjuster: adjuster::identity,
338ec7d4e72SBo Chen     },
339ec7d4e72SBo Chen     PerformanceTest {
340638c4a57SRob Bradford         name: "virtio_net_throughput_single_queue_rx_gbps",
3411a17564eSBo Chen         func_ptr: performance_net_throughput,
3421a17564eSBo Chen         control: PerformanceTestControl {
34318d51d3bSRob Bradford             num_queues: Some(2),
3441a17564eSBo Chen             queue_size: Some(256),
34503d8a3ebSWei Liu             net_control: Some((true, true)),
346ec7d4e72SBo Chen             ..PerformanceTestControl::default()
347ec7d4e72SBo Chen         },
348638c4a57SRob Bradford         unit_adjuster: adjuster::bps_to_gbps,
349ec7d4e72SBo Chen     },
350ec7d4e72SBo Chen     PerformanceTest {
351638c4a57SRob Bradford         name: "virtio_net_throughput_single_queue_tx_gbps",
3521a17564eSBo Chen         func_ptr: performance_net_throughput,
3531a17564eSBo Chen         control: PerformanceTestControl {
35418d51d3bSRob Bradford             num_queues: Some(2),
3551a17564eSBo Chen             queue_size: Some(256),
35603d8a3ebSWei Liu             net_control: Some((false, true)),
357ec7d4e72SBo Chen             ..PerformanceTestControl::default()
358ec7d4e72SBo Chen         },
359638c4a57SRob Bradford         unit_adjuster: adjuster::bps_to_gbps,
360ec7d4e72SBo Chen     },
361ec7d4e72SBo Chen     PerformanceTest {
362638c4a57SRob Bradford         name: "virtio_net_throughput_multi_queue_rx_gbps",
3631a17564eSBo Chen         func_ptr: performance_net_throughput,
3641a17564eSBo Chen         control: PerformanceTestControl {
36518d51d3bSRob Bradford             num_queues: Some(4),
3667c965538SRob Bradford             queue_size: Some(256),
36703d8a3ebSWei Liu             net_control: Some((true, true)),
368ec7d4e72SBo Chen             ..PerformanceTestControl::default()
369ec7d4e72SBo Chen         },
370638c4a57SRob Bradford         unit_adjuster: adjuster::bps_to_gbps,
371ec7d4e72SBo Chen     },
372ec7d4e72SBo Chen     PerformanceTest {
373638c4a57SRob Bradford         name: "virtio_net_throughput_multi_queue_tx_gbps",
3741a17564eSBo Chen         func_ptr: performance_net_throughput,
3751a17564eSBo Chen         control: PerformanceTestControl {
37618d51d3bSRob Bradford             num_queues: Some(4),
3777c965538SRob Bradford             queue_size: Some(256),
37803d8a3ebSWei Liu             net_control: Some((false, true)),
379ec7d4e72SBo Chen             ..PerformanceTestControl::default()
380ec7d4e72SBo Chen         },
381638c4a57SRob Bradford         unit_adjuster: adjuster::bps_to_gbps,
382ec7d4e72SBo Chen     },
383ec7d4e72SBo Chen     PerformanceTest {
38403d8a3ebSWei Liu         name: "virtio_net_throughput_single_queue_rx_pps",
38503d8a3ebSWei Liu         func_ptr: performance_net_throughput,
38603d8a3ebSWei Liu         control: PerformanceTestControl {
38703d8a3ebSWei Liu             num_queues: Some(2),
38803d8a3ebSWei Liu             queue_size: Some(256),
38903d8a3ebSWei Liu             net_control: Some((true, false)),
39003d8a3ebSWei Liu             ..PerformanceTestControl::default()
39103d8a3ebSWei Liu         },
39203d8a3ebSWei Liu         unit_adjuster: adjuster::identity,
39303d8a3ebSWei Liu     },
39403d8a3ebSWei Liu     PerformanceTest {
39503d8a3ebSWei Liu         name: "virtio_net_throughput_single_queue_tx_pps",
39603d8a3ebSWei Liu         func_ptr: performance_net_throughput,
39703d8a3ebSWei Liu         control: PerformanceTestControl {
39803d8a3ebSWei Liu             num_queues: Some(2),
39903d8a3ebSWei Liu             queue_size: Some(256),
40003d8a3ebSWei Liu             net_control: Some((false, false)),
40103d8a3ebSWei Liu             ..PerformanceTestControl::default()
40203d8a3ebSWei Liu         },
40303d8a3ebSWei Liu         unit_adjuster: adjuster::identity,
40403d8a3ebSWei Liu     },
40503d8a3ebSWei Liu     PerformanceTest {
40603d8a3ebSWei Liu         name: "virtio_net_throughput_multi_queue_rx_pps",
40703d8a3ebSWei Liu         func_ptr: performance_net_throughput,
40803d8a3ebSWei Liu         control: PerformanceTestControl {
40903d8a3ebSWei Liu             num_queues: Some(4),
41003d8a3ebSWei Liu             queue_size: Some(256),
41103d8a3ebSWei Liu             net_control: Some((true, false)),
41203d8a3ebSWei Liu             ..PerformanceTestControl::default()
41303d8a3ebSWei Liu         },
41403d8a3ebSWei Liu         unit_adjuster: adjuster::identity,
41503d8a3ebSWei Liu     },
41603d8a3ebSWei Liu     PerformanceTest {
41703d8a3ebSWei Liu         name: "virtio_net_throughput_multi_queue_tx_pps",
41803d8a3ebSWei Liu         func_ptr: performance_net_throughput,
41903d8a3ebSWei Liu         control: PerformanceTestControl {
42003d8a3ebSWei Liu             num_queues: Some(4),
42103d8a3ebSWei Liu             queue_size: Some(256),
42203d8a3ebSWei Liu             net_control: Some((false, false)),
42303d8a3ebSWei Liu             ..PerformanceTestControl::default()
42403d8a3ebSWei Liu         },
42503d8a3ebSWei Liu         unit_adjuster: adjuster::identity,
42603d8a3ebSWei Liu     },
42703d8a3ebSWei Liu     PerformanceTest {
428638c4a57SRob Bradford         name: "block_read_MiBps",
4291a17564eSBo Chen         func_ptr: performance_block_io,
4301a17564eSBo Chen         control: PerformanceTestControl {
4315e3d7267SRob Bradford             num_queues: Some(1),
4327c965538SRob Bradford             queue_size: Some(128),
433e16817eaSWei Liu             fio_control: Some((FioOps::Read, true)),
434ec7d4e72SBo Chen             ..PerformanceTestControl::default()
435ec7d4e72SBo Chen         },
436638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
437ec7d4e72SBo Chen     },
438ec7d4e72SBo Chen     PerformanceTest {
439638c4a57SRob Bradford         name: "block_write_MiBps",
4401a17564eSBo Chen         func_ptr: performance_block_io,
4411a17564eSBo Chen         control: PerformanceTestControl {
4425e3d7267SRob Bradford             num_queues: Some(1),
4437c965538SRob Bradford             queue_size: Some(128),
444e16817eaSWei Liu             fio_control: Some((FioOps::Write, true)),
445ec7d4e72SBo Chen             ..PerformanceTestControl::default()
446ec7d4e72SBo Chen         },
447638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
448ec7d4e72SBo Chen     },
449ec7d4e72SBo Chen     PerformanceTest {
450638c4a57SRob Bradford         name: "block_random_read_MiBps",
4511a17564eSBo Chen         func_ptr: performance_block_io,
4521a17564eSBo Chen         control: PerformanceTestControl {
4535e3d7267SRob Bradford             num_queues: Some(1),
4547c965538SRob Bradford             queue_size: Some(128),
455e16817eaSWei Liu             fio_control: Some((FioOps::RandomRead, true)),
456ec7d4e72SBo Chen             ..PerformanceTestControl::default()
457ec7d4e72SBo Chen         },
458638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
459ec7d4e72SBo Chen     },
460ec7d4e72SBo Chen     PerformanceTest {
461638c4a57SRob Bradford         name: "block_random_write_MiBps",
4621a17564eSBo Chen         func_ptr: performance_block_io,
4631a17564eSBo Chen         control: PerformanceTestControl {
4645e3d7267SRob Bradford             num_queues: Some(1),
4657c965538SRob Bradford             queue_size: Some(128),
466e16817eaSWei Liu             fio_control: Some((FioOps::RandomWrite, true)),
467ec7d4e72SBo Chen             ..PerformanceTestControl::default()
468ec7d4e72SBo Chen         },
469638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
470ec7d4e72SBo Chen     },
471ec7d4e72SBo Chen     PerformanceTest {
472638c4a57SRob Bradford         name: "block_multi_queue_read_MiBps",
4731a17564eSBo Chen         func_ptr: performance_block_io,
4741a17564eSBo Chen         control: PerformanceTestControl {
4755e3d7267SRob Bradford             num_queues: Some(2),
4767c965538SRob Bradford             queue_size: Some(128),
477e16817eaSWei Liu             fio_control: Some((FioOps::Read, true)),
478ec7d4e72SBo Chen             ..PerformanceTestControl::default()
479ec7d4e72SBo Chen         },
480638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
481ec7d4e72SBo Chen     },
482ec7d4e72SBo Chen     PerformanceTest {
483638c4a57SRob Bradford         name: "block_multi_queue_write_MiBps",
4841a17564eSBo Chen         func_ptr: performance_block_io,
4851a17564eSBo Chen         control: PerformanceTestControl {
4865e3d7267SRob Bradford             num_queues: Some(2),
4877c965538SRob Bradford             queue_size: Some(128),
488e16817eaSWei Liu             fio_control: Some((FioOps::Write, true)),
489ec7d4e72SBo Chen             ..PerformanceTestControl::default()
490ec7d4e72SBo Chen         },
491638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
492ec7d4e72SBo Chen     },
493ec7d4e72SBo Chen     PerformanceTest {
494638c4a57SRob Bradford         name: "block_multi_queue_random_read_MiBps",
4951a17564eSBo Chen         func_ptr: performance_block_io,
4961a17564eSBo Chen         control: PerformanceTestControl {
4975e3d7267SRob Bradford             num_queues: Some(2),
4987c965538SRob Bradford             queue_size: Some(128),
499e16817eaSWei Liu             fio_control: Some((FioOps::RandomRead, true)),
500ec7d4e72SBo Chen             ..PerformanceTestControl::default()
501ec7d4e72SBo Chen         },
502638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
503ec7d4e72SBo Chen     },
504ec7d4e72SBo Chen     PerformanceTest {
505638c4a57SRob Bradford         name: "block_multi_queue_random_write_MiBps",
5061a17564eSBo Chen         func_ptr: performance_block_io,
5071a17564eSBo Chen         control: PerformanceTestControl {
5085e3d7267SRob Bradford             num_queues: Some(2),
5097c965538SRob Bradford             queue_size: Some(128),
510e16817eaSWei Liu             fio_control: Some((FioOps::RandomWrite, true)),
511ec7d4e72SBo Chen             ..PerformanceTestControl::default()
512ec7d4e72SBo Chen         },
513638c4a57SRob Bradford         unit_adjuster: adjuster::Bps_to_MiBps,
514ec7d4e72SBo Chen     },
515e16817eaSWei Liu     PerformanceTest {
516e16817eaSWei Liu         name: "block_read_IOPS",
517e16817eaSWei Liu         func_ptr: performance_block_io,
518e16817eaSWei Liu         control: PerformanceTestControl {
519e16817eaSWei Liu             num_queues: Some(1),
520e16817eaSWei Liu             queue_size: Some(128),
521e16817eaSWei Liu             fio_control: Some((FioOps::Read, false)),
522e16817eaSWei Liu             ..PerformanceTestControl::default()
523e16817eaSWei Liu         },
524e16817eaSWei Liu         unit_adjuster: adjuster::identity,
525e16817eaSWei Liu     },
526e16817eaSWei Liu     PerformanceTest {
527e16817eaSWei Liu         name: "block_write_IOPS",
528e16817eaSWei Liu         func_ptr: performance_block_io,
529e16817eaSWei Liu         control: PerformanceTestControl {
530e16817eaSWei Liu             num_queues: Some(1),
531e16817eaSWei Liu             queue_size: Some(128),
532e16817eaSWei Liu             fio_control: Some((FioOps::Write, false)),
533e16817eaSWei Liu             ..PerformanceTestControl::default()
534e16817eaSWei Liu         },
535e16817eaSWei Liu         unit_adjuster: adjuster::identity,
536e16817eaSWei Liu     },
537e16817eaSWei Liu     PerformanceTest {
538e16817eaSWei Liu         name: "block_random_read_IOPS",
539e16817eaSWei Liu         func_ptr: performance_block_io,
540e16817eaSWei Liu         control: PerformanceTestControl {
541e16817eaSWei Liu             num_queues: Some(1),
542e16817eaSWei Liu             queue_size: Some(128),
543e16817eaSWei Liu             fio_control: Some((FioOps::RandomRead, false)),
544e16817eaSWei Liu             ..PerformanceTestControl::default()
545e16817eaSWei Liu         },
546e16817eaSWei Liu         unit_adjuster: adjuster::identity,
547e16817eaSWei Liu     },
548e16817eaSWei Liu     PerformanceTest {
549e16817eaSWei Liu         name: "block_random_write_IOPS",
550e16817eaSWei Liu         func_ptr: performance_block_io,
551e16817eaSWei Liu         control: PerformanceTestControl {
552e16817eaSWei Liu             num_queues: Some(1),
553e16817eaSWei Liu             queue_size: Some(128),
554e16817eaSWei Liu             fio_control: Some((FioOps::RandomWrite, false)),
555e16817eaSWei Liu             ..PerformanceTestControl::default()
556e16817eaSWei Liu         },
557e16817eaSWei Liu         unit_adjuster: adjuster::identity,
558e16817eaSWei Liu     },
559e16817eaSWei Liu     PerformanceTest {
560e16817eaSWei Liu         name: "block_multi_queue_read_IOPS",
561e16817eaSWei Liu         func_ptr: performance_block_io,
562e16817eaSWei Liu         control: PerformanceTestControl {
563e16817eaSWei Liu             num_queues: Some(2),
564e16817eaSWei Liu             queue_size: Some(128),
565e16817eaSWei Liu             fio_control: Some((FioOps::Read, false)),
566e16817eaSWei Liu             ..PerformanceTestControl::default()
567e16817eaSWei Liu         },
568e16817eaSWei Liu         unit_adjuster: adjuster::identity,
569e16817eaSWei Liu     },
570e16817eaSWei Liu     PerformanceTest {
571e16817eaSWei Liu         name: "block_multi_queue_write_IOPS",
572e16817eaSWei Liu         func_ptr: performance_block_io,
573e16817eaSWei Liu         control: PerformanceTestControl {
574e16817eaSWei Liu             num_queues: Some(2),
575e16817eaSWei Liu             queue_size: Some(128),
576e16817eaSWei Liu             fio_control: Some((FioOps::Write, false)),
577e16817eaSWei Liu             ..PerformanceTestControl::default()
578e16817eaSWei Liu         },
579e16817eaSWei Liu         unit_adjuster: adjuster::identity,
580e16817eaSWei Liu     },
581e16817eaSWei Liu     PerformanceTest {
582e16817eaSWei Liu         name: "block_multi_queue_random_read_IOPS",
583e16817eaSWei Liu         func_ptr: performance_block_io,
584e16817eaSWei Liu         control: PerformanceTestControl {
585e16817eaSWei Liu             num_queues: Some(2),
586e16817eaSWei Liu             queue_size: Some(128),
587e16817eaSWei Liu             fio_control: Some((FioOps::RandomRead, false)),
588e16817eaSWei Liu             ..PerformanceTestControl::default()
589e16817eaSWei Liu         },
590e16817eaSWei Liu         unit_adjuster: adjuster::identity,
591e16817eaSWei Liu     },
592e16817eaSWei Liu     PerformanceTest {
593e16817eaSWei Liu         name: "block_multi_queue_random_write_IOPS",
594e16817eaSWei Liu         func_ptr: performance_block_io,
595e16817eaSWei Liu         control: PerformanceTestControl {
596e16817eaSWei Liu             num_queues: Some(2),
597e16817eaSWei Liu             queue_size: Some(128),
598e16817eaSWei Liu             fio_control: Some((FioOps::RandomWrite, false)),
599e16817eaSWei Liu             ..PerformanceTestControl::default()
600e16817eaSWei Liu         },
601e16817eaSWei Liu         unit_adjuster: adjuster::identity,
602e16817eaSWei Liu     },
603ec7d4e72SBo Chen ];
6041a17564eSBo Chen 
run_test_with_timeout( test: &'static PerformanceTest, overrides: &Arc<PerformanceTestOverrides>, ) -> Result<PerformanceTestResult, Error>605025447e8SRob Bradford fn run_test_with_timeout(
606025447e8SRob Bradford     test: &'static PerformanceTest,
607025447e8SRob Bradford     overrides: &Arc<PerformanceTestOverrides>,
608025447e8SRob Bradford ) -> Result<PerformanceTestResult, Error> {
6090862064fSBo Chen     let (sender, receiver) = channel::<Result<PerformanceTestResult, Error>>();
610025447e8SRob Bradford     let test_iterations = overrides.test_iterations;
6117e47464aSsmit-gardhariya     let test_timeout = overrides.test_timeout;
612025447e8SRob Bradford     let overrides = overrides.clone();
6136b915de2SBo Chen     thread::spawn(move || {
614025447e8SRob Bradford         println!(
615025447e8SRob Bradford             "Test '{}' running .. (control: {}, overrides: {})",
616025447e8SRob Bradford             test.name, test.control, overrides
617025447e8SRob Bradford         );
6186b915de2SBo Chen 
619025447e8SRob Bradford         let output = match std::panic::catch_unwind(|| test.run(&overrides)) {
6206b915de2SBo Chen             Ok(test_result) => {
6216b915de2SBo Chen                 println!(
6226b915de2SBo Chen                     "Test '{}' .. ok: mean = {}, std_dev = {}",
6236b915de2SBo Chen                     test_result.name, test_result.mean, test_result.std_dev
6246b915de2SBo Chen                 );
6250862064fSBo Chen                 Ok(test_result)
6261a17564eSBo Chen             }
6276b915de2SBo Chen             Err(_) => Err(Error::TestFailed),
6286b915de2SBo Chen         };
6296b915de2SBo Chen 
6306b915de2SBo Chen         let _ = sender.send(output);
6316b915de2SBo Chen     });
6326b915de2SBo Chen 
6336b915de2SBo Chen     // Todo: Need to cleanup/kill all hanging child processes
6347e47464aSsmit-gardhariya     let test_timeout = test.calc_timeout(&test_iterations, &test_timeout);
6356b915de2SBo Chen     receiver
6366b915de2SBo Chen         .recv_timeout(Duration::from_secs(test_timeout))
6376b915de2SBo Chen         .map_err(|_| {
6386b915de2SBo Chen             eprintln!(
6396b915de2SBo Chen                 "[Error] Test '{}' time-out after {} seconds",
6406b915de2SBo Chen                 test.name, test_timeout
6416b915de2SBo Chen             );
6426b915de2SBo Chen             Error::TestTimeout
6436b915de2SBo Chen         })?
6446b915de2SBo Chen }
6456b915de2SBo Chen 
date() -> String6460862064fSBo Chen fn date() -> String {
6470862064fSBo Chen     let output = test_infra::exec_host_command_output("date");
6480862064fSBo Chen     String::from_utf8_lossy(&output.stdout).trim().to_string()
6490862064fSBo Chen }
6500862064fSBo Chen 
main()6516b915de2SBo Chen fn main() {
652ce902c19SRavi kumar Veeramally     let cmd_arguments = ClapCommand::new("performance-metrics")
653ce902c19SRavi kumar Veeramally         .version(env!("CARGO_PKG_VERSION"))
654ce902c19SRavi kumar Veeramally         .author(env!("CARGO_PKG_AUTHORS"))
655ce902c19SRavi kumar Veeramally         .about("Generate the performance metrics data for Cloud Hypervisor")
656ce902c19SRavi kumar Veeramally         .arg(
657ce902c19SRavi kumar Veeramally             Arg::new("test-filter")
658ce902c19SRavi kumar Veeramally                 .long("test-filter")
659ce902c19SRavi kumar Veeramally                 .help("Filter metrics tests to run based on provided keywords")
660ce902c19SRavi kumar Veeramally                 .num_args(1)
661ce902c19SRavi kumar Veeramally                 .required(false),
662ce902c19SRavi kumar Veeramally         )
663ce902c19SRavi kumar Veeramally         .arg(
664ce902c19SRavi kumar Veeramally             Arg::new("list-tests")
665ce902c19SRavi kumar Veeramally                 .long("list-tests")
666ce902c19SRavi kumar Veeramally                 .help("Print the list of available metrics tests")
667ce902c19SRavi kumar Veeramally                 .num_args(0)
668ce902c19SRavi kumar Veeramally                 .action(ArgAction::SetTrue)
669ce902c19SRavi kumar Veeramally                 .required(false),
670ce902c19SRavi kumar Veeramally         )
671ce902c19SRavi kumar Veeramally         .arg(
672ce902c19SRavi kumar Veeramally             Arg::new("report-file")
673ce902c19SRavi kumar Veeramally                 .long("report-file")
674ce902c19SRavi kumar Veeramally                 .help("Report file. Standard error is used if not specified")
675ce902c19SRavi kumar Veeramally                 .num_args(1),
676ce902c19SRavi kumar Veeramally         )
677ce902c19SRavi kumar Veeramally         .arg(
678ce902c19SRavi kumar Veeramally             Arg::new("iterations")
679ce902c19SRavi kumar Veeramally                 .long("iterations")
680ce902c19SRavi kumar Veeramally                 .help("Override number of test iterations")
681ce902c19SRavi kumar Veeramally                 .num_args(1),
682ce902c19SRavi kumar Veeramally         )
683ce902c19SRavi kumar Veeramally         .arg(
684ce902c19SRavi kumar Veeramally             Arg::new("timeout")
685ce902c19SRavi kumar Veeramally                 .long("timeout")
686ce902c19SRavi kumar Veeramally                 .help("Override test timeout, Ex. --timeout 5")
687ce902c19SRavi kumar Veeramally                 .num_args(1),
688ce902c19SRavi kumar Veeramally         )
689ce902c19SRavi kumar Veeramally         .get_matches();
690411b8b3aSBo Chen 
6918dda052cSHenry Wang     // It seems that the tool (ethr) used for testing the virtio-net latency
6928dda052cSHenry Wang     // is not stable on AArch64, and therefore the latency test is currently
6938dda052cSHenry Wang     // skipped on AArch64.
6948dda052cSHenry Wang     let test_list: Vec<&PerformanceTest> = TEST_LIST
6958dda052cSHenry Wang         .iter()
6968dda052cSHenry Wang         .filter(|t| !(cfg!(target_arch = "aarch64") && t.name == "virtio_net_latency_us"))
6978dda052cSHenry Wang         .collect();
6988dda052cSHenry Wang 
699ce902c19SRavi kumar Veeramally     if cmd_arguments.get_flag("list-tests") {
7008dda052cSHenry Wang         for test in test_list.iter() {
701411b8b3aSBo Chen             println!("\"{}\" ({})", test.name, test.control);
702411b8b3aSBo Chen         }
703411b8b3aSBo Chen 
704411b8b3aSBo Chen         return;
705411b8b3aSBo Chen     }
706411b8b3aSBo Chen 
707ce902c19SRavi kumar Veeramally     let test_filter = match cmd_arguments.get_many::<String>("test-filter") {
708ce902c19SRavi kumar Veeramally         Some(s) => s.collect(),
709ce902c19SRavi kumar Veeramally         None => Vec::new(),
710ce902c19SRavi kumar Veeramally     };
7116b915de2SBo Chen 
7120862064fSBo Chen     // Run performance tests sequentially and report results (in both readable/json format)
7135bd305faSBo Chen     let mut metrics_report: MetricsReport = Default::default();
7140862064fSBo Chen 
715b8069359SBo Chen     init_tests();
716b8069359SBo Chen 
717025447e8SRob Bradford     let overrides = Arc::new(PerformanceTestOverrides {
718ce902c19SRavi kumar Veeramally         test_iterations: cmd_arguments
719ce902c19SRavi kumar Veeramally             .get_one::<String>("iterations")
720ce902c19SRavi kumar Veeramally             .map(|s| s.parse())
721ce902c19SRavi kumar Veeramally             .transpose()
722ce902c19SRavi kumar Veeramally             .unwrap_or_default(),
723ce902c19SRavi kumar Veeramally         test_timeout: cmd_arguments
724ce902c19SRavi kumar Veeramally             .get_one::<String>("timeout")
725ce902c19SRavi kumar Veeramally             .map(|s| s.parse())
726ce902c19SRavi kumar Veeramally             .transpose()
727ce902c19SRavi kumar Veeramally             .unwrap_or_default(),
728025447e8SRob Bradford     });
729025447e8SRob Bradford 
7308dda052cSHenry Wang     for test in test_list.iter() {
731411b8b3aSBo Chen         if test_filter.is_empty() || test_filter.iter().any(|&s| test.name.contains(s)) {
732025447e8SRob Bradford             match run_test_with_timeout(test, &overrides) {
7336b915de2SBo Chen                 Ok(r) => {
7340862064fSBo Chen                     metrics_report.results.push(r);
7356b915de2SBo Chen                 }
7366b915de2SBo Chen                 Err(e) => {
7375e527294SRob Bradford                     eprintln!("Aborting test due to error: '{e:?}'");
738d3be855bSRob Bradford                     std::process::exit(1);
7396b915de2SBo Chen                 }
7406b915de2SBo Chen             };
7416b915de2SBo Chen         }
7426b915de2SBo Chen     }
7436b915de2SBo Chen 
744b8069359SBo Chen     cleanup_tests();
745b8069359SBo Chen 
746ce902c19SRavi kumar Veeramally     let mut report_file: Box<dyn std::io::Write + Send> =
747ce902c19SRavi kumar Veeramally         if let Some(file) = cmd_arguments.get_one::<String>("report-file") {
7481004f870SRob Bradford             Box::new(
7491004f870SRob Bradford                 std::fs::File::create(std::path::Path::new(file))
750c922bf23SRob Bradford                     .map_err(|e| {
7515e527294SRob Bradford                         eprintln!("Error opening report file: {file}: {e}");
752c922bf23SRob Bradford                         std::process::exit(1);
753c922bf23SRob Bradford                     })
7541004f870SRob Bradford                     .unwrap(),
7551004f870SRob Bradford             )
7561004f870SRob Bradford         } else {
757f99c09faSRob Bradford             Box::new(std::io::stdout())
7581004f870SRob Bradford         };
7591004f870SRob Bradford 
760d650c684SBo Chen     report_file
761a6d86b94SRob Bradford         .write_all(
762d650c684SBo Chen             serde_json::to_string_pretty(&metrics_report)
763d650c684SBo Chen                 .unwrap()
764d650c684SBo Chen                 .as_bytes(),
765d650c684SBo Chen         )
766c922bf23SRob Bradford         .map_err(|e| {
7675e527294SRob Bradford             eprintln!("Error writing report file: {e}");
768c922bf23SRob Bradford             std::process::exit(1);
769c922bf23SRob Bradford         })
770d650c684SBo Chen         .unwrap();
7711a17564eSBo Chen }
772