git » libfiu » commit e8c24ec

tests: Add a performance test

author Alberto Bertogli
2013-10-29 02:01:01 UTC
committer Alberto Bertogli
2013-10-29 02:12:13 UTC
parent 5c20f63dba8dae4c46a43af6ff40f0103d653718

tests: Add a performance test

This patch adds a simple performance test which runs "e2fsck" using the posix
preloader and under various conditions, timing how much it takes.

It is useful as a real-life syscall-intensive load test, which can give us an
indication of libfiu's overhead.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>

tests/perf-fsck.py +148 -0

diff --git a/tests/perf-fsck.py b/tests/perf-fsck.py
new file mode 100644
index 0000000..8d367b9
--- /dev/null
+++ b/tests/perf-fsck.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+"""
+Performance tests using fsck.ext2 on a test file.
+
+It can be tuned with the following environment variables:
+
+ - TEST_FILE: The name of the file used for testing. By default, ".test_fs".
+ - TEST_FILE_SIZE_MB: The size of the test file, in megabytes. Only used if
+   the file doesn't exist. Default: 10.
+ - VERBOSE: Show verbose output (from 0 to 2). Default: 0.
+ - LD_LIBRARY_PATH: Library path to pass on to the subcommands.
+   Default: <directory of this file>/../libfiu/, which is usually the one
+   containing the current build.
+"""
+
+import os
+import sys
+import subprocess
+import time
+
+test_file = os.environ.get('TEST_FILE', '.test_fs')
+
+test_file_size = int(os.environ.get('TEST_FILE_SIZE_MB', 10))
+
+ld_library_path = os.environ.get('LD_LIBRARY_PATH',
+        os.path.abspath(
+            os.path.abspath(os.path.dirname(sys.argv[0]))
+            + '/../libfiu/'))
+
+verbose = int(os.environ.get('VERBOSE', 0))
+
+dev_null = open('/dev/null', 'w')
+
+def run_child(name, args, stdout = dev_null, stderr = dev_null):
+    """Runs the subprocess, returns the Popen object."""
+    env = dict(os.environ)
+    env['LD_LIBRARY_PATH'] = ld_library_path
+
+    if verbose and stdout == stderr == dev_null:
+        stdout = stderr = None
+
+    child = subprocess.Popen(args, env = env,
+            stdout = stdout, stderr = stderr)
+    return child
+
+def run_and_time(name, args):
+    """Run the given arguments, print the times."""
+    start = time.time()
+    child = run_child(name, args)
+    _, status, rusage = os.wait4(child.pid, 0)
+    end = time.time()
+
+    if verbose == 2:
+        print 'Ran %s -> %d' % (args, status)
+
+    if status != 0:
+        print 'Error running %s: %s' % (args[0], status)
+        raise RuntimeError
+
+    print '%-10s u:%.3f  s:%.3f  r:%.3f' % (
+            name, rusage.ru_utime, rusage.ru_stime, end - start)
+
+
+def run_fsck(name, fiu_args):
+    """Runs an fsck with the given fiu arguments."""
+    child = run_child(name,
+            ["fiu-run", "-x"] + fiu_args
+                + "fsck.ext2 -n -t -f".split() + [test_file],
+                stdout = subprocess.PIPE,
+                stderr = subprocess.PIPE)
+    stdout, stderr = child.communicate()
+
+    if child.returncode != 0:
+        print 'Error running fsck: %s' % child.returncode
+        raise RuntimeError
+
+    # Find the times reported by fsck.
+    # Not very robust, but useful as it measures the real program time run,
+    # and not the startup overhead.
+    # The line looks like:
+    #   Memory used: 560k/0k (387k/174k), time:  2.18/ 2.17/ 0.00
+    user_time = sys_time = real_time = -1
+    for l in stdout.split('\n'):
+        if not l.startswith("Memory used"):
+            continue
+
+        times = l.split(':')[-1].split('/')
+        times = [s.strip() for s in times]
+        if len(times) != 3:
+            continue
+
+        real_time = float(times[0])
+        user_time = float(times[1])
+        sys_time = float(times[2])
+        break
+
+    print '%-10s u:%.3f  s:%.3f  r:%.3f' % (
+            name, user_time, sys_time, real_time)
+
+
+def check_test_file():
+    if os.path.exists(test_file):
+        return
+
+    with open(test_file, 'w') as fd:
+        fd.truncate(test_file_size * 1024 * 1024)
+
+    retcode = subprocess.call(
+            ["mkfs.ext2", "-F", test_file],
+            stdout = open('/dev/null', 'w'))
+    if retcode != 0:
+        print 'Error running mkfs.ext2:', retcode
+        return
+
+
+if __name__ == '__main__':
+    check_test_file()
+
+    run_and_time("base", "fsck.ext2 -n -f".split() + [test_file])
+
+    # 1 all-matching wildcard.
+    run_fsck("w1", ["-c", "enable_random name=*,probability=0"])
+
+    # 1k final failure points, no matches.
+    args = []
+    for i in range(1000):
+        args += ["-c", "enable_random name=none/%d,probability=0" % i]
+    run_fsck("f1k", args)
+
+    # 1k wildcard failure points, no matches.
+    args = []
+    for i in range(1000):
+        args += ["-c", "enable_random name=none/%d/*,probability=0" % i]
+    run_fsck("w1k", args)
+
+    # 1k wildcarded failure points, and 1 match.
+    args = []
+    for i in range(1000):
+        args += ["-c", "enable_random name=none/%d/*,probability=0" % i]
+    args += ["-c", "enable_random name=*,probability=0"]
+    run_fsck("w1k+1", args)
+
+    # 1k final failure points, *all* matches.
+    args = []
+    for i in range(1000):
+        args += ["-c", "enable_random name=*,probability=0"]
+    run_fsck("m1k", args)