Let's keep view part in separate: this way it's better to improve it in the following commits. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Message-Id: <20201021145859.11201-18-vsementsov@virtuozzo.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Max Reitz <mreitz@redhat.com>
		
			
				
	
	
		
			172 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/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))
 |