Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 7e636a7
  • /
  • tools
  • /
  • cgroup
  • /
  • iocost_coef_gen.py
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:df17a2ae80e5f900576dc0a55af0c2544db6365b
directory badge Iframe embedding
swh:1:dir:97f9b79b96b9de1ec325e4764640e3e548d79fd6
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
iocost_coef_gen.py
#!/usr/bin/env python3
#
# Copyright (C) 2019 Tejun Heo <tj@kernel.org>
# Copyright (C) 2019 Andy Newell <newella@fb.com>
# Copyright (C) 2019 Facebook

desc = """
Generate linear IO cost model coefficients used by the blk-iocost
controller.  If the target raw testdev is specified, destructive tests
are performed against the whole device; otherwise, on
./iocost-coef-fio.testfile.  The result can be written directly to
/sys/fs/cgroup/io.cost.model.

On high performance devices, --numjobs > 1 is needed to achieve
saturation.

See Documentation/admin-guide/cgroup-v2.rst and block/blk-iocost.c
for more details.
"""

import argparse
import re
import json
import glob
import os
import sys
import atexit
import shutil
import tempfile
import subprocess

parser = argparse.ArgumentParser(description=desc,
                                 formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--testdev', metavar='DEV',
                    help='Raw block device to use for testing, ignores --testfile-size')
parser.add_argument('--testfile-size-gb', type=float, metavar='GIGABYTES', default=16,
                    help='Testfile size in gigabytes (default: %(default)s)')
parser.add_argument('--duration', type=int, metavar='SECONDS', default=120,
                    help='Individual test run duration in seconds (default: %(default)s)')
parser.add_argument('--seqio-block-mb', metavar='MEGABYTES', type=int, default=128,
                    help='Sequential test block size in megabytes (default: %(default)s)')
parser.add_argument('--seq-depth', type=int, metavar='DEPTH', default=64,
                    help='Sequential test queue depth (default: %(default)s)')
parser.add_argument('--rand-depth', type=int, metavar='DEPTH', default=64,
                    help='Random test queue depth (default: %(default)s)')
parser.add_argument('--numjobs', type=int, metavar='JOBS', default=1,
                    help='Number of parallel fio jobs to run (default: %(default)s)')
parser.add_argument('--quiet', action='store_true')
parser.add_argument('--verbose', action='store_true')

def info(msg):
    if not args.quiet:
        print(msg)

def dbg(msg):
    if args.verbose and not args.quiet:
        print(msg)

# determine ('DEVNAME', 'MAJ:MIN') for @path
def dir_to_dev(path):
    # find the block device the current directory is on
    devname = subprocess.run(f'findmnt -nvo SOURCE -T{path}',
                             stdout=subprocess.PIPE, shell=True).stdout
    devname = os.path.basename(devname).decode('utf-8').strip()

    # partition -> whole device
    parents = glob.glob('/sys/block/*/' + devname)
    if len(parents):
        devname = os.path.basename(os.path.dirname(parents[0]))
    rdev = os.stat(f'/dev/{devname}').st_rdev
    return (devname, f'{os.major(rdev)}:{os.minor(rdev)}')

def create_testfile(path, size):
    global args

    if os.path.isfile(path) and os.stat(path).st_size == size:
        return

    info(f'Creating testfile {path}')
    subprocess.check_call(f'rm -f {path}', shell=True)
    subprocess.check_call(f'touch {path}', shell=True)
    subprocess.call(f'chattr +C {path}', shell=True)
    subprocess.check_call(
        f'pv -s {size} -pr /dev/urandom {"-q" if args.quiet else ""} | '
        f'dd of={path} count={size} '
        f'iflag=count_bytes,fullblock oflag=direct bs=16M status=none',
        shell=True)

def run_fio(testfile, duration, iotype, iodepth, blocksize, jobs):
    global args

    eta = 'never' if args.quiet else 'always'
    outfile = tempfile.NamedTemporaryFile()
    cmd = (f'fio --direct=1 --ioengine=libaio --name=coef '
           f'--filename={testfile} --runtime={round(duration)} '
           f'--readwrite={iotype} --iodepth={iodepth} --blocksize={blocksize} '
           f'--eta={eta} --output-format json --output={outfile.name} '
           f'--time_based --numjobs={jobs}')
    if args.verbose:
        dbg(f'Running {cmd}')
    subprocess.check_call(cmd, shell=True)
    with open(outfile.name, 'r') as f:
        d = json.loads(f.read())
    return sum(j['read']['bw_bytes'] + j['write']['bw_bytes'] for j in d['jobs'])

def restore_elevator_nomerges():
    global elevator_path, nomerges_path, elevator, nomerges

    info(f'Restoring elevator to {elevator} and nomerges to {nomerges}')
    with open(elevator_path, 'w') as f:
        f.write(elevator)
    with open(nomerges_path, 'w') as f:
        f.write(nomerges)


args = parser.parse_args()

missing = False
for cmd in [ 'findmnt', 'pv', 'dd', 'fio' ]:
    if not shutil.which(cmd):
        print(f'Required command "{cmd}" is missing', file=sys.stderr)
        missing = True
if missing:
    sys.exit(1)

if args.testdev:
    devname = os.path.basename(args.testdev)
    rdev = os.stat(f'/dev/{devname}').st_rdev
    devno = f'{os.major(rdev)}:{os.minor(rdev)}'
    testfile = f'/dev/{devname}'
    info(f'Test target: {devname}({devno})')
else:
    devname, devno = dir_to_dev('.')
    testfile = 'iocost-coef-fio.testfile'
    testfile_size = int(args.testfile_size_gb * 2 ** 30)
    create_testfile(testfile, testfile_size)
    info(f'Test target: {testfile} on {devname}({devno})')

elevator_path = f'/sys/block/{devname}/queue/scheduler'
nomerges_path = f'/sys/block/{devname}/queue/nomerges'

with open(elevator_path, 'r') as f:
    elevator = re.sub(r'.*\[(.*)\].*', r'\1', f.read().strip())
with open(nomerges_path, 'r') as f:
    nomerges = f.read().strip()

info(f'Temporarily disabling elevator and merges')
atexit.register(restore_elevator_nomerges)
with open(elevator_path, 'w') as f:
    f.write('none')
with open(nomerges_path, 'w') as f:
    f.write('1')

info('Determining rbps...')
rbps = run_fio(testfile, args.duration, 'read',
               1, args.seqio_block_mb * (2 ** 20), args.numjobs)
info(f'\nrbps={rbps}, determining rseqiops...')
rseqiops = round(run_fio(testfile, args.duration, 'read',
                         args.seq_depth, 4096, args.numjobs) / 4096)
info(f'\nrseqiops={rseqiops}, determining rrandiops...')
rrandiops = round(run_fio(testfile, args.duration, 'randread',
                          args.rand_depth, 4096, args.numjobs) / 4096)
info(f'\nrrandiops={rrandiops}, determining wbps...')
wbps = run_fio(testfile, args.duration, 'write',
               1, args.seqio_block_mb * (2 ** 20), args.numjobs)
info(f'\nwbps={wbps}, determining wseqiops...')
wseqiops = round(run_fio(testfile, args.duration, 'write',
                         args.seq_depth, 4096, args.numjobs) / 4096)
info(f'\nwseqiops={wseqiops}, determining wrandiops...')
wrandiops = round(run_fio(testfile, args.duration, 'randwrite',
                          args.rand_depth, 4096, args.numjobs) / 4096)
info(f'\nwrandiops={wrandiops}')
restore_elevator_nomerges()
atexit.unregister(restore_elevator_nomerges)
info('')

print(f'{devno} rbps={rbps} rseqiops={rseqiops} rrandiops={rrandiops} '
      f'wbps={wbps} wseqiops={wseqiops} wrandiops={wrandiops}')

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top