3 # Test to compare performance of write requests for two qemu-img binary files.
5 # The idea of the test comes from intention to check the benefit of c8bb23cbdbe
6 # "qcow2: skip writing zero buffers to empty COW areas".
8 # Copyright (c) 2020 Virtuozzo International GmbH.
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
31 def bench_func(env
, case
):
32 """ Handle one "cell" of benchmarking table. """
33 return bench_write_req(env
['qemu_img'], env
['image_name'],
34 case
['block_size'], case
['block_offset'],
38 def qemu_img_pipe(*args
):
39 '''Run qemu-img and return its output'''
40 subp
= subprocess
.Popen(list(args
),
41 stdout
=subprocess
.PIPE
,
42 stderr
=subprocess
.STDOUT
,
43 universal_newlines
=True)
44 exitcode
= subp
.wait()
46 sys
.stderr
.write('qemu-img received signal %i: %s\n'
47 % (-exitcode
, ' '.join(list(args
))))
48 return subp
.communicate()[0]
51 def bench_write_req(qemu_img
, image_name
, block_size
, block_offset
,
53 """Benchmark write requests
55 The function creates a QCOW2 image with the given path/name. Then it runs
56 the 'qemu-img bench' command and makes series of write requests on the
57 image clusters. Finally, it returns the total time of the write operations
60 qemu_img -- path to qemu_img executable file
61 image_name -- QCOW2 image name to create
62 block_size -- size of a block to write to clusters
63 block_offset -- offset of the block in clusters
64 cluster_size -- size of the image cluster
66 Returns {'seconds': int} on success and {'error': str} on failure.
67 Return value is compatible with simplebench lib.
70 if not os
.path
.isfile(qemu_img
):
71 print(f
'File not found: {qemu_img}')
74 image_dir
= os
.path
.dirname(os
.path
.abspath(image_name
))
75 if not os
.path
.isdir(image_dir
):
76 print(f
'Path not found: {image_name}')
79 image_size
= 1024 * 1024 * 1024
81 args_create
= [qemu_img
, 'create', '-f', 'qcow2', '-o',
82 f
'cluster_size={cluster_size}',
83 image_name
, str(image_size
)]
85 count
= int(image_size
/ cluster_size
) - 1
86 step
= str(cluster_size
)
88 args_bench
= [qemu_img
, 'bench', '-w', '-n', '-t', 'none', '-c',
89 str(count
), '-s', f
'{block_size}', '-o', str(block_offset
),
90 '-S', step
, '-f', 'qcow2', image_name
]
93 qemu_img_pipe(*args_create
)
96 return {'error': 'qemu_img create failed: ' + str(e
)}
99 ret
= qemu_img_pipe(*args_bench
)
101 os
.remove(image_name
)
102 return {'error': 'qemu_img bench failed: ' + str(e
)}
104 os
.remove(image_name
)
107 ret_list
= ret
.split()
108 index
= ret_list
.index('seconds.')
109 return {'seconds': float(ret_list
[index
-1])}
111 return {'error': 'qemu_img bench failed: ' + ret
}
114 if __name__
== '__main__':
116 if len(sys
.argv
) < 4:
117 program
= os
.path
.basename(sys
.argv
[0])
118 print(f
'USAGE: {program} <path to qemu-img binary file> '
119 '<path to another qemu-img to compare performance with> '
120 '<full or relative name for QCOW2 image to create>')
123 # Test-cases are "rows" in benchmark resulting table, 'id' is a caption
124 # for the row, other fields are handled by bench_func.
127 'id': '<cluster front>',
130 'cluster_size': 1048576
133 'id': '<cluster middle>',
135 'block_offset': 524288,
136 'cluster_size': 1048576
139 'id': '<cross cluster>',
140 'block_size': 1048576,
141 'block_offset': 4096,
142 'cluster_size': 1048576
145 'id': '<cluster 64K>',
148 'cluster_size': 65536
152 # Test-envs are "columns" in benchmark resulting table, 'id is a caption
153 # for the column, other fields are handled by bench_func.
154 # Set the paths below to desired values
157 'id': '<qemu-img binary 1>',
158 'qemu_img': f
'{sys.argv[1]}',
159 'image_name': f
'{sys.argv[3]}'
162 'id': '<qemu-img binary 2>',
163 'qemu_img': f
'{sys.argv[2]}',
164 'image_name': f
'{sys.argv[3]}'
168 result
= simplebench
.bench(bench_func
, test_envs
, test_cases
, count
=3,
170 print(simplebench
.results_to_text(result
))