1*cff6d3caSVladimir Sementsov-Ogievskiy#!/usr/bin/env python3
2*cff6d3caSVladimir Sementsov-Ogievskiy#
3*cff6d3caSVladimir Sementsov-Ogievskiy# Benchmark preallocate filter
4*cff6d3caSVladimir Sementsov-Ogievskiy#
5*cff6d3caSVladimir Sementsov-Ogievskiy# Copyright (c) 2020 Virtuozzo International GmbH.
6*cff6d3caSVladimir Sementsov-Ogievskiy#
7*cff6d3caSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify
8*cff6d3caSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by
9*cff6d3caSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or
10*cff6d3caSVladimir Sementsov-Ogievskiy# (at your option) any later version.
11*cff6d3caSVladimir Sementsov-Ogievskiy#
12*cff6d3caSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful,
13*cff6d3caSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of
14*cff6d3caSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*cff6d3caSVladimir Sementsov-Ogievskiy# GNU General Public License for more details.
16*cff6d3caSVladimir Sementsov-Ogievskiy#
17*cff6d3caSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License
18*cff6d3caSVladimir Sementsov-Ogievskiy# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*cff6d3caSVladimir Sementsov-Ogievskiy#
20*cff6d3caSVladimir Sementsov-Ogievskiy
21*cff6d3caSVladimir Sementsov-Ogievskiy
22*cff6d3caSVladimir Sementsov-Ogievskiyimport sys
23*cff6d3caSVladimir Sementsov-Ogievskiyimport os
24*cff6d3caSVladimir Sementsov-Ogievskiyimport subprocess
25*cff6d3caSVladimir Sementsov-Ogievskiyimport re
26*cff6d3caSVladimir Sementsov-Ogievskiyimport json
27*cff6d3caSVladimir Sementsov-Ogievskiy
28*cff6d3caSVladimir Sementsov-Ogievskiyimport simplebench
29*cff6d3caSVladimir Sementsov-Ogievskiyfrom results_to_text import results_to_text
30*cff6d3caSVladimir Sementsov-Ogievskiy
31*cff6d3caSVladimir Sementsov-Ogievskiy
32*cff6d3caSVladimir Sementsov-Ogievskiydef qemu_img_bench(args):
33*cff6d3caSVladimir Sementsov-Ogievskiy    p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
34*cff6d3caSVladimir Sementsov-Ogievskiy                       universal_newlines=True)
35*cff6d3caSVladimir Sementsov-Ogievskiy
36*cff6d3caSVladimir Sementsov-Ogievskiy    if p.returncode == 0:
37*cff6d3caSVladimir Sementsov-Ogievskiy        try:
38*cff6d3caSVladimir Sementsov-Ogievskiy            m = re.search(r'Run completed in (\d+.\d+) seconds.', p.stdout)
39*cff6d3caSVladimir Sementsov-Ogievskiy            return {'seconds': float(m.group(1))}
40*cff6d3caSVladimir Sementsov-Ogievskiy        except Exception:
41*cff6d3caSVladimir Sementsov-Ogievskiy            return {'error': f'failed to parse qemu-img output: {p.stdout}'}
42*cff6d3caSVladimir Sementsov-Ogievskiy    else:
43*cff6d3caSVladimir Sementsov-Ogievskiy        return {'error': f'qemu-img failed: {p.returncode}: {p.stdout}'}
44*cff6d3caSVladimir Sementsov-Ogievskiy
45*cff6d3caSVladimir Sementsov-Ogievskiy
46*cff6d3caSVladimir Sementsov-Ogievskiydef bench_func(env, case):
47*cff6d3caSVladimir Sementsov-Ogievskiy    fname = f"{case['dir']}/prealloc-test.qcow2"
48*cff6d3caSVladimir Sementsov-Ogievskiy    try:
49*cff6d3caSVladimir Sementsov-Ogievskiy        os.remove(fname)
50*cff6d3caSVladimir Sementsov-Ogievskiy    except OSError:
51*cff6d3caSVladimir Sementsov-Ogievskiy        pass
52*cff6d3caSVladimir Sementsov-Ogievskiy
53*cff6d3caSVladimir Sementsov-Ogievskiy    subprocess.run([env['qemu-img-binary'], 'create', '-f', 'qcow2', fname,
54*cff6d3caSVladimir Sementsov-Ogievskiy                   '16G'], stdout=subprocess.DEVNULL,
55*cff6d3caSVladimir Sementsov-Ogievskiy                   stderr=subprocess.DEVNULL, check=True)
56*cff6d3caSVladimir Sementsov-Ogievskiy
57*cff6d3caSVladimir Sementsov-Ogievskiy    args = [env['qemu-img-binary'], 'bench', '-c', str(case['count']),
58*cff6d3caSVladimir Sementsov-Ogievskiy            '-d', '64', '-s', case['block-size'], '-t', 'none', '-n', '-w']
59*cff6d3caSVladimir Sementsov-Ogievskiy    if env['prealloc']:
60*cff6d3caSVladimir Sementsov-Ogievskiy        args += ['--image-opts',
61*cff6d3caSVladimir Sementsov-Ogievskiy                 'driver=qcow2,file.driver=preallocate,file.file.driver=file,'
62*cff6d3caSVladimir Sementsov-Ogievskiy                 f'file.file.filename={fname}']
63*cff6d3caSVladimir Sementsov-Ogievskiy    else:
64*cff6d3caSVladimir Sementsov-Ogievskiy        args += ['-f', 'qcow2', fname]
65*cff6d3caSVladimir Sementsov-Ogievskiy
66*cff6d3caSVladimir Sementsov-Ogievskiy    return qemu_img_bench(args)
67*cff6d3caSVladimir Sementsov-Ogievskiy
68*cff6d3caSVladimir Sementsov-Ogievskiy
69*cff6d3caSVladimir Sementsov-Ogievskiydef auto_count_bench_func(env, case):
70*cff6d3caSVladimir Sementsov-Ogievskiy    case['count'] = 100
71*cff6d3caSVladimir Sementsov-Ogievskiy    while True:
72*cff6d3caSVladimir Sementsov-Ogievskiy        res = bench_func(env, case)
73*cff6d3caSVladimir Sementsov-Ogievskiy        if 'error' in res:
74*cff6d3caSVladimir Sementsov-Ogievskiy            return res
75*cff6d3caSVladimir Sementsov-Ogievskiy
76*cff6d3caSVladimir Sementsov-Ogievskiy        if res['seconds'] >= 1:
77*cff6d3caSVladimir Sementsov-Ogievskiy            break
78*cff6d3caSVladimir Sementsov-Ogievskiy
79*cff6d3caSVladimir Sementsov-Ogievskiy        case['count'] *= 10
80*cff6d3caSVladimir Sementsov-Ogievskiy
81*cff6d3caSVladimir Sementsov-Ogievskiy    if res['seconds'] < 5:
82*cff6d3caSVladimir Sementsov-Ogievskiy        case['count'] = round(case['count'] * 5 / res['seconds'])
83*cff6d3caSVladimir Sementsov-Ogievskiy        res = bench_func(env, case)
84*cff6d3caSVladimir Sementsov-Ogievskiy        if 'error' in res:
85*cff6d3caSVladimir Sementsov-Ogievskiy            return res
86*cff6d3caSVladimir Sementsov-Ogievskiy
87*cff6d3caSVladimir Sementsov-Ogievskiy    res['iops'] = case['count'] / res['seconds']
88*cff6d3caSVladimir Sementsov-Ogievskiy    return res
89*cff6d3caSVladimir Sementsov-Ogievskiy
90*cff6d3caSVladimir Sementsov-Ogievskiy
91*cff6d3caSVladimir Sementsov-Ogievskiyif __name__ == '__main__':
92*cff6d3caSVladimir Sementsov-Ogievskiy    if len(sys.argv) < 2:
93*cff6d3caSVladimir Sementsov-Ogievskiy        print(f'USAGE: {sys.argv[0]} <qemu-img binary> '
94*cff6d3caSVladimir Sementsov-Ogievskiy              'DISK_NAME:DIR_PATH ...')
95*cff6d3caSVladimir Sementsov-Ogievskiy        exit(1)
96*cff6d3caSVladimir Sementsov-Ogievskiy
97*cff6d3caSVladimir Sementsov-Ogievskiy    qemu_img = sys.argv[1]
98*cff6d3caSVladimir Sementsov-Ogievskiy
99*cff6d3caSVladimir Sementsov-Ogievskiy    envs = [
100*cff6d3caSVladimir Sementsov-Ogievskiy        {
101*cff6d3caSVladimir Sementsov-Ogievskiy            'id': 'no-prealloc',
102*cff6d3caSVladimir Sementsov-Ogievskiy            'qemu-img-binary': qemu_img,
103*cff6d3caSVladimir Sementsov-Ogievskiy            'prealloc': False
104*cff6d3caSVladimir Sementsov-Ogievskiy        },
105*cff6d3caSVladimir Sementsov-Ogievskiy        {
106*cff6d3caSVladimir Sementsov-Ogievskiy            'id': 'prealloc',
107*cff6d3caSVladimir Sementsov-Ogievskiy            'qemu-img-binary': qemu_img,
108*cff6d3caSVladimir Sementsov-Ogievskiy            'prealloc': True
109*cff6d3caSVladimir Sementsov-Ogievskiy        }
110*cff6d3caSVladimir Sementsov-Ogievskiy    ]
111*cff6d3caSVladimir Sementsov-Ogievskiy
112*cff6d3caSVladimir Sementsov-Ogievskiy    aligned_cases = []
113*cff6d3caSVladimir Sementsov-Ogievskiy    unaligned_cases = []
114*cff6d3caSVladimir Sementsov-Ogievskiy
115*cff6d3caSVladimir Sementsov-Ogievskiy    for disk in sys.argv[2:]:
116*cff6d3caSVladimir Sementsov-Ogievskiy        name, path = disk.split(':')
117*cff6d3caSVladimir Sementsov-Ogievskiy        aligned_cases.append({
118*cff6d3caSVladimir Sementsov-Ogievskiy            'id': f'{name}, aligned sequential 16k',
119*cff6d3caSVladimir Sementsov-Ogievskiy            'block-size': '16k',
120*cff6d3caSVladimir Sementsov-Ogievskiy            'dir': path
121*cff6d3caSVladimir Sementsov-Ogievskiy        })
122*cff6d3caSVladimir Sementsov-Ogievskiy        unaligned_cases.append({
123*cff6d3caSVladimir Sementsov-Ogievskiy            'id': f'{name}, unaligned sequential 64k',
124*cff6d3caSVladimir Sementsov-Ogievskiy            'block-size': '16k',
125*cff6d3caSVladimir Sementsov-Ogievskiy            'dir': path
126*cff6d3caSVladimir Sementsov-Ogievskiy        })
127*cff6d3caSVladimir Sementsov-Ogievskiy
128*cff6d3caSVladimir Sementsov-Ogievskiy    result = simplebench.bench(auto_count_bench_func, envs,
129*cff6d3caSVladimir Sementsov-Ogievskiy                               aligned_cases + unaligned_cases, count=5)
130*cff6d3caSVladimir Sementsov-Ogievskiy    print(results_to_text(result))
131*cff6d3caSVladimir Sementsov-Ogievskiy    with open('results.json', 'w') as f:
132*cff6d3caSVladimir Sementsov-Ogievskiy        json.dump(result, f, indent=4)
133