| #!/usr/bin/env python3 |
| # |
| # Process img-bench test templates |
| # |
| # Copyright (c) 2021 Virtuozzo International GmbH. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| # |
| |
| |
| import sys |
| import subprocess |
| import re |
| import json |
| |
| import simplebench |
| from results_to_text import results_to_text |
| from table_templater import Templater |
| |
| |
| def bench_func(env, case): |
| test = templater.gen(env['data'], case['data']) |
| |
| p = subprocess.run(test, shell=True, stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, universal_newlines=True) |
| |
| if p.returncode == 0: |
| try: |
| m = re.search(r'Run completed in (\d+.\d+) seconds.', p.stdout) |
| return {'seconds': float(m.group(1))} |
| except Exception: |
| return {'error': f'failed to parse qemu-img output: {p.stdout}'} |
| else: |
| return {'error': f'qemu-img failed: {p.returncode}: {p.stdout}'} |
| |
| |
| if __name__ == '__main__': |
| if len(sys.argv) > 1: |
| print(""" |
| Usage: img_bench_templater.py < path/to/test-template.sh |
| |
| This script generates performance tests from a test template (example below), |
| runs them, and displays the results in a table. The template is read from |
| stdin. It must be written in bash and end with a `qemu-img bench` invocation |
| (whose result is parsed to get the test instance’s result). |
| |
| Use the following syntax in the template to create the various different test |
| instances: |
| |
| column templating: {var1|var2|...} - test will use different values in |
| different columns. You may use several {} constructions in the test, in this |
| case product of all choice-sets will be used. |
| |
| row templating: [var1|var2|...] - similar thing to define rows (test-cases) |
| |
| Test template example: |
| |
| Assume you want to compare two qemu-img binaries, called qemu-img-old and |
| qemu-img-new in your build directory in two test-cases with 4K writes and 64K |
| writes. The template may look like this: |
| |
| qemu_img=/path/to/qemu/build/qemu-img-{old|new} |
| $qemu_img create -f qcow2 /ssd/x.qcow2 1G |
| $qemu_img bench -c 100 -d 8 [-s 4K|-s 64K] -w -t none -n /ssd/x.qcow2 |
| |
| When passing this to stdin of img_bench_templater.py, the resulting comparison |
| table will contain two columns (for two binaries) and two rows (for two |
| test-cases). |
| |
| In addition to displaying the results, script also stores results in JSON |
| format into results.json file in current directory. |
| """) |
| sys.exit() |
| |
| templater = Templater(sys.stdin.read()) |
| |
| envs = [{'id': ' / '.join(x), 'data': x} for x in templater.columns] |
| cases = [{'id': ' / '.join(x), 'data': x} for x in templater.rows] |
| |
| result = simplebench.bench(bench_func, envs, cases, count=5, |
| initial_run=False) |
| print(results_to_text(result)) |
| with open('results.json', 'w') as f: |
| json.dump(result, f, indent=4) |