qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

bench_write_req.py (5664B)


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