diff --git a/Makefile.am b/Makefile.am index bccae3d0c9af4d6dae2fbafd431d7cbbf6d93691..b645caba99f0431ad6aea347c98dc8cc51d4a3e8 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 0000000000000000000000000000000000000000..dddd019e2dbdfae3c98d08ee96c3cf8a6de74803 --- /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()