1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
#!/usr/bin/env python3
#
# Test to compare performance of write requests for two qemu-img binary files.
#
# The idea of the test comes from intention to check the benefit of c8bb23cbdbe
# "qcow2: skip writing zero buffers to empty COW areas".
#
# Copyright (c) 2020 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 os
import subprocess
import simplebench
from results_to_text import results_to_text
def bench_func(env, case):
""" Handle one "cell" of benchmarking table. """
return bench_write_req(env['qemu_img'], env['image_name'],
case['block_size'], case['block_offset'],
case['cluster_size'])
def qemu_img_pipe(*args):
'''Run qemu-img and return its output'''
subp = subprocess.Popen(list(args),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
exitcode = subp.wait()
if exitcode < 0:
sys.stderr.write('qemu-img received signal %i: %s\n'
% (-exitcode, ' '.join(list(args))))
return subp.communicate()[0]
def bench_write_req(qemu_img, image_name, block_size, block_offset,
cluster_size):
"""Benchmark write requests
The function creates a QCOW2 image with the given path/name. Then it runs
the 'qemu-img bench' command and makes series of write requests on the
image clusters. Finally, it returns the total time of the write operations
on the disk.
qemu_img -- path to qemu_img executable file
image_name -- QCOW2 image name to create
block_size -- size of a block to write to clusters
block_offset -- offset of the block in clusters
cluster_size -- size of the image cluster
Returns {'seconds': int} on success and {'error': str} on failure.
Return value is compatible with simplebench lib.
"""
if not os.path.isfile(qemu_img):
print(f'File not found: {qemu_img}')
sys.exit(1)
image_dir = os.path.dirname(os.path.abspath(image_name))
if not os.path.isdir(image_dir):
print(f'Path not found: {image_name}')
sys.exit(1)
image_size = 1024 * 1024 * 1024
args_create = [qemu_img, 'create', '-f', 'qcow2', '-o',
f'cluster_size={cluster_size}',
image_name, str(image_size)]
count = int(image_size / cluster_size) - 1
step = str(cluster_size)
args_bench = [qemu_img, 'bench', '-w', '-n', '-t', 'none', '-c',
str(count), '-s', f'{block_size}', '-o', str(block_offset),
'-S', step, '-f', 'qcow2', image_name]
try:
qemu_img_pipe(*args_create)
except OSError as e:
os.remove(image_name)
return {'error': 'qemu_img create failed: ' + str(e)}
try:
ret = qemu_img_pipe(*args_bench)
except OSError as e:
os.remove(image_name)
return {'error': 'qemu_img bench failed: ' + str(e)}
os.remove(image_name)
if 'seconds' in ret:
ret_list = ret.split()
index = ret_list.index('seconds.')
return {'seconds': float(ret_list[index-1])}
else:
return {'error': 'qemu_img bench failed: ' + ret}
if __name__ == '__main__':
if len(sys.argv) < 4:
program = os.path.basename(sys.argv[0])
print(f'USAGE: {program} <path to qemu-img binary file> '
'<path to another qemu-img to compare performance with> '
'<full or relative name for QCOW2 image to create>')
exit(1)
# Test-cases are "rows" in benchmark resulting table, 'id' is a caption
# for the row, other fields are handled by bench_func.
test_cases = [
{
'id': '<cluster front>',
'block_size': 4096,
'block_offset': 0,
'cluster_size': 1048576
},
{
'id': '<cluster middle>',
'block_size': 4096,
'block_offset': 524288,
'cluster_size': 1048576
},
{
'id': '<cross cluster>',
'block_size': 1048576,
'block_offset': 4096,
'cluster_size': 1048576
},
{
'id': '<cluster 64K>',
'block_size': 4096,
'block_offset': 0,
'cluster_size': 65536
},
]
# Test-envs are "columns" in benchmark resulting table, 'id is a caption
# for the column, other fields are handled by bench_func.
# Set the paths below to desired values
test_envs = [
{
'id': '<qemu-img binary 1>',
'qemu_img': f'{sys.argv[1]}',
'image_name': f'{sys.argv[3]}'
},
{
'id': '<qemu-img binary 2>',
'qemu_img': f'{sys.argv[2]}',
'image_name': f'{sys.argv[3]}'
},
]
result = simplebench.bench(bench_func, test_envs, test_cases, count=3,
initial_run=False)
print(results_to_text(result))
|