#!/usr/bin/python # (c) Alexander Belchenko, 2007, 2009 """Benchmarking. Run each test 3 times and get median value. Using 10K array as base test time. Each other test compared with base with next formula:: Tc * Nb q = --------- Tb * Nc Here: * Tc - execution time of current test * Tb - execution time of base * Nb - array size of base (10K) * Nc - array size of current test If resulting value is ``q <= 1.0`` it's the best possible result, i.e. time increase proportionally to array size. """ from cStringIO import StringIO import gc import sys import time import intelhex def median(values): """Return median value for the list of values. @param values: list of values for processing. @return: median value. """ values.sort() n = int(len(values) / 2) return values[n] def run_test(func, fobj): """Run func with argument fobj and measure execution time. @param func: function for test @param fobj: data for test @return: execution time """ gc.disable() try: begin = time.time() func(fobj) end = time.time() finally: gc.enable() return end - begin def run_readtest_N_times(func, hexstr, n): """Run each test N times. @param func: function for test @param hexstr: string with content of hex file to read @param n: times to repeat. @return: (median time, times list) """ assert n > 0 times = [] for i in range(n): sio = StringIO(hexstr) times.append(run_test(func, sio)) sio.close() t = median(times) return t, times def run_writetest_N_times(func, n): """Run each test N times. @param func: function for test @param n: times to repeat. @return: (median time, times list) """ assert n > 0 times = [] for i in range(n): sio = StringIO() times.append(run_test(func, sio)) sio.close() t = median(times) return t, times def time_coef(tc, nc, tb, nb): """Return time coefficient relative to base numbers. @param tc: current test time @param nc: current test data size @param tb: base test time @param nb: base test data size @return: time coef. """ tc = float(tc) nc = float(nc) tb = float(tb) nb = float(nb) q = (tc * nb) / (tb * nc) return q def get_test_data(n1, offset, n2): """Create test data on given pattern. @param n1: size of first part of array at base address 0. @param offset: offset for second part of array. @param n2: size of second part of array at given offset. @return: (overall size, hex file, IntelHex object) """ # make IntelHex object ih = intelhex.IntelHex() addr = 0 for i in range(n1): ih[addr] = addr % 256 addr += 1 addr += offset for i in range(n2): ih[addr] = addr % 256 addr += 1 # make hex file sio = StringIO() ih.write_hex_file(sio) hexstr = sio.getvalue() sio.close() # return n1+n2, hexstr, ih def get_base_10K(): """Base 10K""" return get_test_data(10000, 0, 0) def get_100K(): return get_test_data(100000, 0, 0) def get_100K_100K(): return get_test_data(100000, 1000000, 100000) def get_0_100K(): return get_test_data(0, 1000000, 100000) def get_1M(): return get_test_data(1000000, 0, 0) class Measure(object): """Measure execution time helper.""" data_set = [ # (data name, getter) ('base 10K', get_base_10K), # first should be base numbers ('100K', get_100K), ('1M', get_1M), ('100K+100K', get_100K_100K), ('0+100K', get_0_100K), ] def __init__(self, n=3, read=True, write=True): self.n = n self.read = read self.write = write self.results = [] def measure_one(self, data): """Do measuring of read and write operations. @param data: 3-tuple from get_test_data @return: (time readhex, time writehex) """ _unused, hexstr, ih = data tread, twrite = 0.0, 0.0 if self.read: tread = run_readtest_N_times(intelhex.IntelHex, hexstr, self.n)[0] if self.write: twrite = run_writetest_N_times(ih.write_hex_file, self.n)[0] return tread, twrite def measure_all(self): for name, getter in self.data_set: data = getter() times = self.measure_one(data) self.results.append((name, times, data[0])) def print_report(self, to_file=None): if to_file is None: to_file = sys.stdout base_title, base_times, base_n = self.results[0] base_read, base_write = base_times read_report = ['%-10s\t%7.3f' % (base_title, base_read)] write_report = ['%-10s\t%7.3f' % (base_title, base_write)] for item in self.results[1:]: cur_title, cur_times, cur_n = item cur_read, cur_write = cur_times if self.read: qread = time_coef(cur_read, cur_n, base_read, base_n) read_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, cur_read, qread)) if self.write: qwrite = time_coef(cur_write, cur_n, base_write, base_n) write_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, cur_write, qwrite)) if self.read: to_file.write('Read operation:\n') to_file.write('\n'.join(read_report)) to_file.write('\n\n') if self.write: to_file.write('Write operation:\n') to_file.write('\n'.join(write_report)) to_file.write('\n\n') HELP = """\ Usage: python _bench.py [OPTIONS] Options: -h this help -n N repeat tests N times -r run only tests for read operation -w run only tests for write operation If option -r or -w is not specified then all tests will be run. """ def main(argv=None): """Main function to run benchmarks. @param argv: command-line arguments. @return: exit code (0 is OK). """ import getopt # default values test_read = None test_write = None n = 3 # number of repeat if argv is None: argv = sys.argv[1:] try: opts, args = getopt.getopt(argv, 'hn:rw', []) for o,a in opts: if o == '-h': print(HELP) return 0 elif o == '-n': n = int(a) elif o == '-r': test_read = True elif o == '-w': test_write = True if args: raise getopt.GetoptError('Arguments are not used.') except getopt.GetoptError, msg: txt = str(msg) print(txt) return 1 if (test_read, test_write) == (None, None): test_read = test_write = True m = Measure(n, test_read, test_write) m.measure_all() m.print_report() return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) """ Some Results ************ 21/04/2007 revno.40 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.031 100K 0.360 1.161 1M 3.500 1.129 100K+100K 0.719 1.160 0+100K 0.360 1.161 Write operation: base 10K 0.031 100K 0.297 0.958 1M 2.953 0.953 100K+100K 1.328 2.142 0+100K 0.312 1.006 21/04/2007 revno.46 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.203 1.269 1M 2.000 1.250 100K+100K 0.422 1.319 0+100K 0.203 1.269 Write operation: base 10K 0.031 100K 0.297 0.958 1M 2.969 0.958 100K+100K 1.328 2.142 0+100K 0.312 1.006 22/04/2007 revno.48 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.187 1.169 1M 1.891 1.182 100K+100K 0.406 1.269 0+100K 0.188 1.175 Write operation: base 10K 0.031 100K 0.296 0.955 1M 2.969 0.958 100K+100K 1.328 2.142 0+100K 0.312 1.006 19/08/2008 revno.72 Python 2.5.2 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.171 1.069 1M 1.734 1.084 100K+100K 0.375 1.172 0+100K 0.172 1.075 Write operation: base 10K 0.016 100K 0.156 0.975 1M 1.532 0.957 100K+100K 0.344 1.075 0+100K 0.156 0.975 """