From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: qemu-devel@nongnu.org, qemu-block@nongnu.org
Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, ehabkost@redhat.com,
stefanha@gmail.com, mreitz@redhat.com, crosa@redhat.com,
den@openvz.org, jsnow@redhat.com
Subject: [PATCH for-5.0 v2 1/3] python: add simplebench.py
Date: Tue, 26 Nov 2019 18:48:46 +0300 [thread overview]
Message-ID: <20191126154848.193407-2-vsementsov@virtuozzo.com> (raw)
In-Reply-To: <20191126154848.193407-1-vsementsov@virtuozzo.com>
Add simple benchmark table creator.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
python/simplebench.py | 128 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
create mode 100644 python/simplebench.py
diff --git a/python/simplebench.py b/python/simplebench.py
new file mode 100644
index 0000000000..59e7314ff6
--- /dev/null
+++ b/python/simplebench.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+#
+# Simple benchmarking framework
+#
+# Copyright (c) 2019 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/>.
+#
+
+
+def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
+ """Benchmark one test-case
+
+ test_func -- benchmarking function with prototype
+ test_func(env, case), which takes test_env and test_case
+ arguments and returns {'seconds': int} (which is benchmark
+ result) on success and {'error': str} on error. Returned
+ dict may contain any other additional fields.
+ test_env -- test environment - opaque first argument for test_func
+ test_case -- test case - opaque second argument for test_func
+ count -- how many times to call test_func, to calculate average
+ initial_run -- do initial run of test_func, which don't get into result
+
+ Returns dict with the following fields:
+ 'runs': list of test_func results
+ 'average': average seconds per run (exists only if at least one run
+ succeeded)
+ 'delta': maximum delta between test_func result and the average
+ (exists only if at least one run succeeded)
+ 'n-failed': number of failed runs (exists only if at least one run
+ failed)
+ """
+ if initial_run:
+ print(' #initial run:')
+ print(' ', test_func(test_env, test_case))
+
+ runs = []
+ for i in range(count):
+ print(' #run {}'.format(i+1))
+ res = test_func(test_env, test_case)
+ print(' ', res)
+ runs.append(res)
+
+ result = {'runs': runs}
+
+ successed = [r for r in runs if ('seconds' in r)]
+ if successed:
+ avg = sum(r['seconds'] for r in successed) / len(successed)
+ result['average'] = avg
+ result['delta'] = max(abs(r['seconds'] - avg) for r in successed)
+
+ if len(successed) < count:
+ result['n-failed'] = count - len(successed)
+
+ return result
+
+
+def ascii_one(result):
+ """Return ASCII representation of bench_one() returned dict."""
+ if 'average' in result:
+ s = '{:.2f} +- {:.2f}'.format(result['average'], result['delta'])
+ if 'n-failed' in result:
+ s += '\n({} failed)'.format(result['n-failed'])
+ return s
+ else:
+ return 'FAILED'
+
+
+def bench(test_func, test_envs, test_cases, *args, **vargs):
+ """Fill benchmark table
+
+ test_func -- benchmarking function, see bench_one for description
+ test_envs -- list of test environments, see bench_one
+ test_cases -- list of test cases, see bench_one
+ args, vargs -- additional arguments for bench_one
+
+ Returns dict with the following fields:
+ 'envs': test_envs
+ 'cases': test_cases
+ 'tab': filled 2D array, where cell [i][j] is bench_one result for
+ test_cases[i] for test_envs[j] (i.e., rows are test cases and
+ columns are test environments)
+ """
+ tab = {}
+ results = {
+ 'envs': test_envs,
+ 'cases': test_cases,
+ 'tab': tab
+ }
+ n = 1
+ n_tests = len(test_envs) * len(test_cases)
+ for env in test_envs:
+ for case in test_cases:
+ print('Testing {}/{}: {} :: {}'.format(n, n_tests,
+ env['id'], case['id']))
+ if case['id'] not in tab:
+ tab[case['id']] = {}
+ tab[case['id']][env['id']] = bench_one(test_func, env, case,
+ *args, **vargs)
+ n += 1
+
+ print('Done')
+ return results
+
+
+def ascii(results):
+ """Return ASCII representation of bench() returned dict."""
+ from tabulate import tabulate
+
+ tab = [[""] + [c['id'] for c in results['envs']]]
+ for case in results['cases']:
+ row = [case['id']]
+ for env in results['envs']:
+ row.append(ascii_one(results['tab'][case['id']][env['id']]))
+ tab.append(row)
+
+ return tabulate(tab)
--
2.18.0
next prev parent reply other threads:[~2019-11-26 15:54 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-11-26 15:48 [PATCH for-5.0 v2 0/3] benchmark util Vladimir Sementsov-Ogievskiy
2019-11-26 15:48 ` Vladimir Sementsov-Ogievskiy [this message]
2019-11-26 15:48 ` [PATCH for-5.0 v2 2/3] python: add qemu/bench_block_job.py Vladimir Sementsov-Ogievskiy
2019-11-26 15:48 ` [PATCH for-5.0 v2 3/3] python: add example usage of simplebench Vladimir Sementsov-Ogievskiy
2019-11-27 19:58 ` [PATCH for-5.0 v2 0/3] benchmark util Aleksandar Markovic
2019-11-27 20:22 ` Vladimir Sementsov-Ogievskiy
2019-12-06 19:43 ` Cleber Rosa
2019-12-06 20:00 ` Vladimir Sementsov-Ogievskiy
2019-12-19 8:22 ` Vladimir Sementsov-Ogievskiy
2020-01-20 9:10 ` ping " Vladimir Sementsov-Ogievskiy
2020-02-08 10:36 ` Vladimir Sementsov-Ogievskiy
2020-02-27 13:18 ` Vladimir Sementsov-Ogievskiy
2020-02-27 20:09 ` Eduardo Habkost
2020-02-28 6:24 ` Vladimir Sementsov-Ogievskiy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191126154848.193407-2-vsementsov@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=crosa@redhat.com \
--cc=den@openvz.org \
--cc=ehabkost@redhat.com \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).