|
3 | 3 | """Performance benchmark for block device emulation.""" |
4 | 4 |
|
5 | 5 | import concurrent |
| 6 | +import glob |
6 | 7 | import os |
7 | 8 | import shutil |
8 | 9 | from pathlib import Path |
@@ -68,6 +69,7 @@ def run_fio(microvm, mode, block_size): |
68 | 69 | # Instruct fio to pin one worker per vcpu |
69 | 70 | .with_arg("--cpus_allowed_policy=split") |
70 | 71 | .with_arg(f"--write_bw_log={mode}") |
| 72 | + .with_arg(f"--write_lat_log={mode}") |
71 | 73 | .with_arg("--log_avg_msec=1000") |
72 | 74 | .build() |
73 | 75 | ) |
@@ -101,37 +103,51 @@ def run_fio(microvm, mode, block_size): |
101 | 103 | return logs_path, cpu_load_future.result() |
102 | 104 |
|
103 | 105 |
|
104 | | -def process_fio_logs(vm, fio_mode, logs_dir, metrics): |
105 | | - """Parses the fio logs in `{logs_dir}/{fio_mode}_bw.*.log and emits their contents as CloudWatch metrics""" |
106 | | - |
| 106 | +def process_fio_log_files(logs_glob): |
| 107 | + """Parses all fio log files matching the given glob and yields tuples of same-timestamp read and write metrics""" |
107 | 108 | data = [ |
108 | | - Path(f"{logs_dir}/{fio_mode}_bw.{job_id + 1}.log") |
109 | | - .read_text("UTF-8") |
110 | | - .splitlines() |
111 | | - for job_id in range(vm.vcpus_count) |
| 109 | + Path(pathname).read_text("UTF-8").splitlines() |
| 110 | + for pathname in glob.glob(logs_glob) |
112 | 111 | ] |
113 | 112 |
|
| 113 | + assert data, "no log files found!" |
| 114 | + |
114 | 115 | for tup in zip(*data): |
115 | | - bw_read = 0 |
116 | | - bw_write = 0 |
| 116 | + read_values = [] |
| 117 | + write_values = [] |
117 | 118 |
|
118 | 119 | for line in tup: |
| 120 | + # See https://fio.readthedocs.io/en/latest/fio_doc.html#log-file-formats |
119 | 121 | _, value, direction, _ = line.split(",", maxsplit=3) |
120 | 122 | value = int(value.strip()) |
121 | 123 |
|
122 | | - # See https://fio.readthedocs.io/en/latest/fio_doc.html#log-file-formats |
123 | 124 | match direction.strip(): |
124 | 125 | case "0": |
125 | | - bw_read += value |
| 126 | + read_values.append(value) |
126 | 127 | case "1": |
127 | | - bw_write += value |
| 128 | + write_values.append(value) |
128 | 129 | case _: |
129 | 130 | assert False |
130 | 131 |
|
| 132 | + yield read_values, write_values |
| 133 | + |
| 134 | + |
| 135 | +def emit_fio_metrics(logs_dir, metrics): |
| 136 | + """Parses the fio logs in `{logs_dir}/*_[bw|lat].*.log and emits their contents as CloudWatch metrics""" |
| 137 | + |
| 138 | + for bw_read, bw_write in process_fio_log_files(f"{logs_dir}/*_bw.*.log"): |
131 | 139 | if bw_read: |
132 | | - metrics.put_metric("bw_read", bw_read, "Kilobytes/Second") |
| 140 | + metrics.put_metric("bw_read", sum(bw_read), "Kilobytes/Second") |
133 | 141 | if bw_write: |
134 | | - metrics.put_metric("bw_write", bw_write, "Kilobytes/Second") |
| 142 | + metrics.put_metric("bw_write", sum(bw_write), "Kilobytes/Second") |
| 143 | + |
| 144 | + for lat_read, lat_write in process_fio_log_files(f"{logs_dir}/*_clat.*.log"): |
| 145 | + # latency values in fio logs are in nanosecons, but cloudwatch only supports |
| 146 | + # microseconds as the more granular unit, so need to divide by 1000. |
| 147 | + for value in lat_read: |
| 148 | + metrics.put_metric("clat_read", value / 1000, "Microseconds") |
| 149 | + for value in lat_write: |
| 150 | + metrics.put_metric("clat_write", value / 1000, "Microseconds") |
135 | 151 |
|
136 | 152 |
|
137 | 153 | @pytest.mark.timeout(120) |
@@ -177,7 +193,7 @@ def test_block_performance( |
177 | 193 |
|
178 | 194 | logs_dir, cpu_util = run_fio(vm, fio_mode, fio_block_size) |
179 | 195 |
|
180 | | - process_fio_logs(vm, fio_mode, logs_dir, metrics) |
| 196 | + emit_fio_metrics(logs_dir, metrics) |
181 | 197 |
|
182 | 198 | for thread_name, values in cpu_util.items(): |
183 | 199 | for value in values: |
@@ -226,7 +242,7 @@ def test_block_vhost_user_performance( |
226 | 242 |
|
227 | 243 | logs_dir, cpu_util = run_fio(vm, fio_mode, fio_block_size) |
228 | 244 |
|
229 | | - process_fio_logs(vm, fio_mode, logs_dir, metrics) |
| 245 | + emit_fio_metrics(logs_dir, metrics) |
230 | 246 |
|
231 | 247 | for thread_name, values in cpu_util.items(): |
232 | 248 | for value in values: |
|
0 commit comments