xref: /qemu/scripts/simplebench/img_bench_templater.py (revision 57b6f58c1d0df757c9311496c32d502925056894)
1*5b3f7daaSVladimir Sementsov-Ogievskiy#!/usr/bin/env python3
2*5b3f7daaSVladimir Sementsov-Ogievskiy#
3*5b3f7daaSVladimir Sementsov-Ogievskiy# Process img-bench test templates
4*5b3f7daaSVladimir Sementsov-Ogievskiy#
5*5b3f7daaSVladimir Sementsov-Ogievskiy# Copyright (c) 2021 Virtuozzo International GmbH.
6*5b3f7daaSVladimir Sementsov-Ogievskiy#
7*5b3f7daaSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify
8*5b3f7daaSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by
9*5b3f7daaSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or
10*5b3f7daaSVladimir Sementsov-Ogievskiy# (at your option) any later version.
11*5b3f7daaSVladimir Sementsov-Ogievskiy#
12*5b3f7daaSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful,
13*5b3f7daaSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of
14*5b3f7daaSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*5b3f7daaSVladimir Sementsov-Ogievskiy# GNU General Public License for more details.
16*5b3f7daaSVladimir Sementsov-Ogievskiy#
17*5b3f7daaSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License
18*5b3f7daaSVladimir Sementsov-Ogievskiy# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*5b3f7daaSVladimir Sementsov-Ogievskiy#
20*5b3f7daaSVladimir Sementsov-Ogievskiy
21*5b3f7daaSVladimir Sementsov-Ogievskiy
22*5b3f7daaSVladimir Sementsov-Ogievskiyimport sys
23*5b3f7daaSVladimir Sementsov-Ogievskiyimport subprocess
24*5b3f7daaSVladimir Sementsov-Ogievskiyimport re
25*5b3f7daaSVladimir Sementsov-Ogievskiyimport json
26*5b3f7daaSVladimir Sementsov-Ogievskiy
27*5b3f7daaSVladimir Sementsov-Ogievskiyimport simplebench
28*5b3f7daaSVladimir Sementsov-Ogievskiyfrom results_to_text import results_to_text
29*5b3f7daaSVladimir Sementsov-Ogievskiyfrom table_templater import Templater
30*5b3f7daaSVladimir Sementsov-Ogievskiy
31*5b3f7daaSVladimir Sementsov-Ogievskiy
32*5b3f7daaSVladimir Sementsov-Ogievskiydef bench_func(env, case):
33*5b3f7daaSVladimir Sementsov-Ogievskiy    test = templater.gen(env['data'], case['data'])
34*5b3f7daaSVladimir Sementsov-Ogievskiy
35*5b3f7daaSVladimir Sementsov-Ogievskiy    p = subprocess.run(test, shell=True, stdout=subprocess.PIPE,
36*5b3f7daaSVladimir Sementsov-Ogievskiy                       stderr=subprocess.STDOUT, universal_newlines=True)
37*5b3f7daaSVladimir Sementsov-Ogievskiy
38*5b3f7daaSVladimir Sementsov-Ogievskiy    if p.returncode == 0:
39*5b3f7daaSVladimir Sementsov-Ogievskiy        try:
40*5b3f7daaSVladimir Sementsov-Ogievskiy            m = re.search(r'Run completed in (\d+.\d+) seconds.', p.stdout)
41*5b3f7daaSVladimir Sementsov-Ogievskiy            return {'seconds': float(m.group(1))}
42*5b3f7daaSVladimir Sementsov-Ogievskiy        except Exception:
43*5b3f7daaSVladimir Sementsov-Ogievskiy            return {'error': f'failed to parse qemu-img output: {p.stdout}'}
44*5b3f7daaSVladimir Sementsov-Ogievskiy    else:
45*5b3f7daaSVladimir Sementsov-Ogievskiy        return {'error': f'qemu-img failed: {p.returncode}: {p.stdout}'}
46*5b3f7daaSVladimir Sementsov-Ogievskiy
47*5b3f7daaSVladimir Sementsov-Ogievskiy
48*5b3f7daaSVladimir Sementsov-Ogievskiyif __name__ == '__main__':
49*5b3f7daaSVladimir Sementsov-Ogievskiy    if len(sys.argv) > 1:
50*5b3f7daaSVladimir Sementsov-Ogievskiy        print("""
51*5b3f7daaSVladimir Sementsov-OgievskiyUsage: img_bench_templater.py < path/to/test-template.sh
52*5b3f7daaSVladimir Sementsov-Ogievskiy
53*5b3f7daaSVladimir Sementsov-OgievskiyThis script generates performance tests from a test template (example below),
54*5b3f7daaSVladimir Sementsov-Ogievskiyruns them, and displays the results in a table. The template is read from
55*5b3f7daaSVladimir Sementsov-Ogievskiystdin.  It must be written in bash and end with a `qemu-img bench` invocation
56*5b3f7daaSVladimir Sementsov-Ogievskiy(whose result is parsed to get the test instance’s result).
57*5b3f7daaSVladimir Sementsov-Ogievskiy
58*5b3f7daaSVladimir Sementsov-OgievskiyUse the following syntax in the template to create the various different test
59*5b3f7daaSVladimir Sementsov-Ogievskiyinstances:
60*5b3f7daaSVladimir Sementsov-Ogievskiy
61*5b3f7daaSVladimir Sementsov-Ogievskiy  column templating: {var1|var2|...} - test will use different values in
62*5b3f7daaSVladimir Sementsov-Ogievskiy  different columns. You may use several {} constructions in the test, in this
63*5b3f7daaSVladimir Sementsov-Ogievskiy  case product of all choice-sets will be used.
64*5b3f7daaSVladimir Sementsov-Ogievskiy
65*5b3f7daaSVladimir Sementsov-Ogievskiy  row templating: [var1|var2|...] - similar thing to define rows (test-cases)
66*5b3f7daaSVladimir Sementsov-Ogievskiy
67*5b3f7daaSVladimir Sementsov-OgievskiyTest template example:
68*5b3f7daaSVladimir Sementsov-Ogievskiy
69*5b3f7daaSVladimir Sementsov-OgievskiyAssume you want to compare two qemu-img binaries, called qemu-img-old and
70*5b3f7daaSVladimir Sementsov-Ogievskiyqemu-img-new in your build directory in two test-cases with 4K writes and 64K
71*5b3f7daaSVladimir Sementsov-Ogievskiywrites. The template may look like this:
72*5b3f7daaSVladimir Sementsov-Ogievskiy
73*5b3f7daaSVladimir Sementsov-Ogievskiyqemu_img=/path/to/qemu/build/qemu-img-{old|new}
74*5b3f7daaSVladimir Sementsov-Ogievskiy$qemu_img create -f qcow2 /ssd/x.qcow2 1G
75*5b3f7daaSVladimir Sementsov-Ogievskiy$qemu_img bench -c 100 -d 8 [-s 4K|-s 64K] -w -t none -n /ssd/x.qcow2
76*5b3f7daaSVladimir Sementsov-Ogievskiy
77*5b3f7daaSVladimir Sementsov-OgievskiyWhen passing this to stdin of img_bench_templater.py, the resulting comparison
78*5b3f7daaSVladimir Sementsov-Ogievskiytable will contain two columns (for two binaries) and two rows (for two
79*5b3f7daaSVladimir Sementsov-Ogievskiytest-cases).
80*5b3f7daaSVladimir Sementsov-Ogievskiy
81*5b3f7daaSVladimir Sementsov-OgievskiyIn addition to displaying the results, script also stores results in JSON
82*5b3f7daaSVladimir Sementsov-Ogievskiyformat into results.json file in current directory.
83*5b3f7daaSVladimir Sementsov-Ogievskiy""")
84*5b3f7daaSVladimir Sementsov-Ogievskiy        sys.exit()
85*5b3f7daaSVladimir Sementsov-Ogievskiy
86*5b3f7daaSVladimir Sementsov-Ogievskiy    templater = Templater(sys.stdin.read())
87*5b3f7daaSVladimir Sementsov-Ogievskiy
88*5b3f7daaSVladimir Sementsov-Ogievskiy    envs = [{'id': ' / '.join(x), 'data': x} for x in templater.columns]
89*5b3f7daaSVladimir Sementsov-Ogievskiy    cases = [{'id': ' / '.join(x), 'data': x} for x in templater.rows]
90*5b3f7daaSVladimir Sementsov-Ogievskiy
91*5b3f7daaSVladimir Sementsov-Ogievskiy    result = simplebench.bench(bench_func, envs, cases, count=5,
92*5b3f7daaSVladimir Sementsov-Ogievskiy                               initial_run=False)
93*5b3f7daaSVladimir Sementsov-Ogievskiy    print(results_to_text(result))
94*5b3f7daaSVladimir Sementsov-Ogievskiy    with open('results.json', 'w') as f:
95*5b3f7daaSVladimir Sementsov-Ogievskiy        json.dump(result, f, indent=4)
96