33"""Performance benchmark for block device emulation."""
44
55import concurrent
6- import glob
76import os
8- from pathlib import Path
97
108import pytest
119
10+ import framework .utils_fio as fio
1211import host_tools .drive as drive_tools
13- from framework .utils import CmdBuilder , check_output , track_cpu_utilization
12+ from framework .utils import check_output , track_cpu_utilization
1413
1514# size of the block device used in the test, in MB
1615BLOCK_DEVICE_SIZE_MB = 2048
@@ -44,41 +43,21 @@ def prepare_microvm_for_test(microvm):
4443 check_output ("echo 3 > /proc/sys/vm/drop_caches" )
4544
4645
47- def run_fio (microvm , mode , block_size , test_output_dir , fio_engine = "libaio" ):
46+ def run_fio (
47+ microvm , mode : fio .Mode , block_size : int , test_output_dir , fio_engine : fio .Engine
48+ ):
4849 """Run a fio test in the specified mode with block size bs."""
49- cmd = (
50- CmdBuilder ("fio" )
51- .with_arg (f"--name={ mode } -{ block_size } " )
52- .with_arg (f"--numjobs={ microvm .vcpus_count } " )
53- .with_arg (f"--runtime={ RUNTIME_SEC } " )
54- .with_arg ("--time_based=1" )
55- .with_arg (f"--ramp_time={ WARMUP_SEC } " )
56- .with_arg ("--filename=/dev/vdb" )
57- .with_arg ("--direct=1" )
58- .with_arg (f"--rw={ mode } " )
59- .with_arg ("--randrepeat=0" )
60- .with_arg (f"--bs={ block_size } " )
61- .with_arg (f"--size={ BLOCK_DEVICE_SIZE_MB } M" )
62- .with_arg (f"--ioengine={ fio_engine } " )
63- .with_arg ("--iodepth=32" )
64- # Set affinity of the entire fio process to a set of vCPUs equal in size to number of workers
65- .with_arg (
66- f"--cpus_allowed={ ',' .join (str (i ) for i in range (microvm .vcpus_count ))} "
67- )
68- # Instruct fio to pin one worker per vcpu
69- .with_arg ("--cpus_allowed_policy=split" )
70- .with_arg ("--log_avg_msec=1000" )
71- .with_arg (f"--write_bw_log={ mode } " )
72- .with_arg ("--output-format=json+" )
73- .with_arg ("--output=/tmp/fio.json" )
50+ cmd = fio .build_cmd (
51+ "/dev/vdb" ,
52+ BLOCK_DEVICE_SIZE_MB ,
53+ block_size ,
54+ mode ,
55+ microvm .vcpus_count ,
56+ fio_engine ,
57+ RUNTIME_SEC ,
58+ WARMUP_SEC ,
7459 )
7560
76- # Latency measurements only make sence for psync engine
77- if fio_engine == "psync" :
78- cmd = cmd .with_arg (f"--write_lat_log={ mode } " )
79-
80- cmd = cmd .build ()
81-
8261 prepare_microvm_for_test (microvm )
8362
8463 # Start the CPU load monitor.
@@ -101,65 +80,30 @@ def run_fio(microvm, mode, block_size, test_output_dir, fio_engine="libaio"):
10180 return cpu_load_future .result ()
10281
10382
104- def process_fio_log_files (root_dir , logs_glob ):
105- """
106- Parses all fio log files in the root_dir matching the given glob and
107- yields tuples of same-timestamp read and write metrics
108- """
109- # We specify `root_dir` for `glob.glob` because otherwise it will
110- # struggle with directory with names like:
111- # test_block_performance[vmlinux-5.10.233-Sync-bs4096-randread-1vcpu]
112- data = [
113- Path (root_dir / pathname ).read_text ("UTF-8" ).splitlines ()
114- for pathname in glob .glob (logs_glob , root_dir = root_dir )
115- ]
116-
117- # If not data found, there is nothing to iterate over
118- if not data :
119- return [], []
120-
121- for tup in zip (* data ):
122- read_values = []
123- write_values = []
124-
125- for line in tup :
126- # See https://fio.readthedocs.io/en/latest/fio_doc.html#log-file-formats
127- _ , value , direction , _ = line .split ("," , maxsplit = 3 )
128- value = int (value .strip ())
129-
130- match direction .strip ():
131- case "0" :
132- read_values .append (value )
133- case "1" :
134- write_values .append (value )
135- case _:
136- assert False
137-
138- yield read_values , write_values
139-
140-
14183def emit_fio_metrics (logs_dir , metrics ):
142- """Parses the fio logs in `{logs_dir}/*_[clat|bw].*.log and emits their contents as CloudWatch metrics"""
143- for bw_read , bw_write in process_fio_log_files (logs_dir , "*_bw.*.log" ):
144- if bw_read :
145- metrics .put_metric ("bw_read" , sum (bw_read ), "Kilobytes/Second" )
146- if bw_write :
147- metrics .put_metric ("bw_write" , sum (bw_write ), "Kilobytes/Second" )
148-
149- for lat_read , lat_write in process_fio_log_files (logs_dir , "*_clat.*.log" ):
150- # latency values in fio logs are in nanoseconds, but cloudwatch only supports
151- # microseconds as the more granular unit, so need to divide by 1000.
152- for value in lat_read :
84+ """Parses the fio logs in `logs_dir` and emits their contents as CloudWatch metrics"""
85+ bw_reads , bw_writes = fio .process_log_files (logs_dir , fio .LogType .BW )
86+ for tup in zip (* bw_reads ):
87+ metrics .put_metric ("bw_read" , sum (tup ), "Kilobytes/Second" )
88+ for tup in zip (* bw_writes ):
89+ metrics .put_metric ("bw_write" , sum (tup ), "Kilobytes/Second" )
90+
91+ clat_reads , clat_writes = fio .process_log_files (logs_dir , fio .LogType .CLAT )
92+ # latency values in fio logs are in nanoseconds, but cloudwatch only supports
93+ # microseconds as the more granular unit, so need to divide by 1000.
94+ for tup in zip (* clat_reads ):
95+ for value in tup :
15396 metrics .put_metric ("clat_read" , value / 1000 , "Microseconds" )
154- for value in lat_write :
97+ for tup in zip (* clat_writes ):
98+ for value in tup :
15599 metrics .put_metric ("clat_write" , value / 1000 , "Microseconds" )
156100
157101
158102@pytest .mark .nonci
159103@pytest .mark .parametrize ("vcpus" , [1 , 2 ], ids = ["1vcpu" , "2vcpu" ])
160- @pytest .mark .parametrize ("fio_mode" , ["randread" , "randwrite" ])
104+ @pytest .mark .parametrize ("fio_mode" , [fio . Mode . RANDREAD , fio . Mode . RANDWRITE ])
161105@pytest .mark .parametrize ("fio_block_size" , [4096 ], ids = ["bs4096" ])
162- @pytest .mark .parametrize ("fio_engine" , ["libaio" , "psync" ])
106+ @pytest .mark .parametrize ("fio_engine" , [fio . Engine . LIBAIO , fio . Engine . PSYNC ])
163107def test_block_performance (
164108 uvm_plain_acpi ,
165109 vcpus ,
@@ -208,7 +152,7 @@ def test_block_performance(
208152
209153@pytest .mark .nonci
210154@pytest .mark .parametrize ("vcpus" , [1 , 2 ], ids = ["1vcpu" , "2vcpu" ])
211- @pytest .mark .parametrize ("fio_mode" , ["randread" ])
155+ @pytest .mark .parametrize ("fio_mode" , [fio . Mode . RANDREAD ])
212156@pytest .mark .parametrize ("fio_block_size" , [4096 ], ids = ["bs4096" ])
213157def test_block_vhost_user_performance (
214158 uvm_plain_acpi ,
@@ -246,7 +190,7 @@ def test_block_vhost_user_performance(
246190 next_cpu = vm .pin_threads (0 )
247191 vm .disks_vhost_user ["scratch" ].pin (next_cpu )
248192
249- cpu_util = run_fio (vm , fio_mode , fio_block_size , results_dir )
193+ cpu_util = run_fio (vm , fio_mode , fio_block_size , results_dir , fio . Engine . LIBAIO )
250194
251195 emit_fio_metrics (results_dir , metrics )
252196
0 commit comments