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