From e4d452b4f896b0b7eb87dae696fdf0796e481525 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Thu, 22 Dec 2011 14:16:57 +0100 Subject: [PATCH] Add lock performance utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I had an idea for improving locking performance. To see if it worked I wrote this tool. Unfortunately the idea didn't quite work (broke unittests left and right), but the tool is still handy for evaluating future changes to the βSharedLockβ class. It is not installed or run at build/test time. In its current form it is intended for manual use. Example output: --- Total number of acquisitions: 32642 Per-thread acquisitions: Thread 0: 6536 (20.0%) Thread 1: 6488 (19.9%) Thread 2: 6536 (20.0%) Thread 3: 6529 (20.0%) Thread 4: 6553 (20.1%) Benchmark CPU time: 5.010s Average time per lock acquisition: 0.15348ms Process: User time: 4.160s System time: 1.030s Total time: 5.190s --- Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: Iustin Pop <iustin@google.com> --- Makefile.am | 1 + test/lockperf.py | 145 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100755 test/lockperf.py diff --git a/Makefile.am b/Makefile.am index bccae3d0c..b645caba9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -587,6 +587,7 @@ EXTRA_DIST = \ doc/examples/gnt-debug/README \ doc/examples/gnt-debug/delay0.json \ doc/examples/gnt-debug/delay50.json \ + test/lockperf.py \ test/testutils.py \ test/mocks.py \ $(dist_TESTS) \ diff --git a/test/lockperf.py b/test/lockperf.py new file mode 100755 index 000000000..dddd019e2 --- /dev/null +++ b/test/lockperf.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing lock performance""" + +import os +import sys +import time +import optparse +import threading +import resource + +from ganeti import locking + + +def ParseOptions(): + """Parses the command line options. + + In case of command line errors, it will show the usage and exit the + program. + + @return: the options in a tuple + + """ + parser = optparse.OptionParser() + parser.add_option("-t", dest="thread_count", default=1, type="int", + help="Number of threads", metavar="NUM") + parser.add_option("-d", dest="duration", default=5, type="float", + help="Duration", metavar="SECS") + + (opts, args) = parser.parse_args() + + if opts.thread_count < 1: + parser.error("Number of threads must be at least 1") + + return (opts, args) + + +class State: + def __init__(self, thread_count): + """Initializes this class. + + """ + self.verify = [False for _ in range(thread_count)] + self.counts = [0 for _ in range(thread_count)] + self.total_count = 0 + + +def _Counter(lock, state, me): + """Thread function for acquiring locks. + + """ + counts = state.counts + verify = state.verify + + while True: + lock.acquire() + try: + verify[me] = 1 + + counts[me] += 1 + + state.total_count += 1 + + if state.total_count % 1000 == 0: + sys.stdout.write(" %8d\r" % state.total_count) + sys.stdout.flush() + + if sum(verify) != 1: + print "Inconsistent state!" + os._exit(1) # pylint: disable=W0212 + + verify[me] = 0 + finally: + lock.release() + + +def main(): + (opts, _) = ParseOptions() + + lock = locking.SharedLock("TestLock") + + state = State(opts.thread_count) + + lock.acquire(shared=0) + try: + for i in range(opts.thread_count): + t = threading.Thread(target=_Counter, args=(lock, state, i)) + t.setDaemon(True) + t.start() + + start = time.clock() + finally: + lock.release() + + while True: + if (time.clock() - start) > opts.duration: + break + time.sleep(0.1) + + # Make sure we get a consistent view + lock.acquire(shared=0) + + lock_cputime = time.clock() - start + + res = resource.getrusage(resource.RUSAGE_SELF) + + print "Total number of acquisitions: %s" % state.total_count + print "Per-thread acquisitions:" + for (i, count) in enumerate(state.counts): + print (" Thread %s: %d (%0.1f%%)" % + (i, count, (100.0 * count / state.total_count))) + + print "Benchmark CPU time: %0.3fs" % lock_cputime + print ("Average time per lock acquisition: %0.5fms" % + (1000.0 * lock_cputime / state.total_count)) + print "Process:" + print " User time: %0.3fs" % res.ru_utime + print " System time: %0.3fs" % res.ru_stime + print " Total time: %0.3fs" % (res.ru_utime + res.ru_stime) + + # Exit directly without attempting to clean up threads + os._exit(0) # pylint: disable=W0212 + + +if __name__ == "__main__": + main() -- GitLab