diff --git a/bleachermark.py b/bleachermark.py index e319701..afe5d90 100644 --- a/bleachermark.py +++ b/bleachermark.py @@ -55,7 +55,12 @@ ('stupid benchmark', 'zero', 1, 0.0, 0)] """ -from time import clock +# Support both Python 2 (using clock) and Python 3 (using perf_counter) +try: + from time import perf_counter +except (ImportError, AttributeError): + from time import clock as perf_counter + from copy import copy #This part handles the ctrl-c interruption. @@ -147,9 +152,9 @@ def run(self, i): time_vals = [i] intervalue = i for fun in self._pipeline: - tim = clock() + tim = perf_counter() intervalue = fun(intervalue) - time_vals.append( (clock()-tim, intervalue) ) + time_vals.append( (perf_counter()-tim, intervalue) ) return time_vals @@ -308,7 +313,7 @@ def fetch_data(self, format="dict"): data.append( (label, fun_labels[i], run[0], m[0], m[1]) ) return data else: - raise ValueError("Invalid argument to format: %s".format(format)) + raise ValueError(f"Invalid argument to format: {format}") def timings(self, transposed=False): r""" @@ -344,11 +349,8 @@ def averages(self): """ timings = self.timings(transposed=True) - res = {} - for bm in timings.keys(): - totals = map(lambda a: sum(a)/len(a), timings[bm]) - res[bm] = totals - return res + return {bm: [sum(t)/len(t) for t in timings[bm]] + for bm in timings.keys() } def variances(self): r""" @@ -375,21 +377,24 @@ def stdvs(self): """ variances = self.variances() import math - return {bm:map(math.sqrt, variances[bm]) for bm in variances} + return {bm: [math.sqrt(v) for v in variances[bm]] + for bm in variances} def maxes(self): r""" Return the maximum running times of the benchmarks run. """ timings = self.timings(transposed=True) - return {bm:map(max, timings[bm]) for bm in timings} + return {bm: [max(t) for t in timings[bm]] + for bm in timings} def mins(self): r""" Return the minimum running times of the benchmarks run. """ timings = self.timings(transposed=True) - return {bm:map(min, timings[bm]) for bm in timings} + return {bm: [min(t) for t in timings[bm]] + for bm in timings} def pipeline_data(self): r""" @@ -427,8 +432,155 @@ def __add__(self, other): return self +class SimpleBleachermark: + """ + Create a collection of benchmarks to evaluate the complexity of a function. + + INPUT: + + - ``data_generator`` -- a function taking a size argument, and + generating some data of this size at random + - ``function`` -- a function taking data generated by ``data_generator`` as input + - ``sizes`` -- a collection of sizes + + EXAMPLES: + + We want to evaluate the practical complexity of Python's sorting + algorithms according to the size of the list. First we write a + function to generate a random list of a give size:: + + >>> from random import randint + >>> def random_list(n): + ... return [randint(0, n) for i in range(n)] + + Then we create the collection of benchmarks:: + + >>> from bleachermark import * + >>> BB = SimpleBleachermark(random_list, sorted, sizes=[1,2,4,8]) + + We run the benchmark:: + + >>> BB.run() + + The benchmark can be interrupted anytime with ^C, and resumed + later on by calling ``run`` again. + + Now we can look at the timings:: + + >>> BB.timings() # random + {1: [6.000000000061512e-06, ... 5.000000000032756e-06], + 2: [4.000000000004e-06, ... 2.9999999999752447e-06], + 4: [4.999999999921734e-06, ... 9.000000000036756e-06], + 8: [5.000000000032756e-06, ... 5.000000000032756e-06]} + + and do some simple statistics on them:: + + >>> BB.averages() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + >>> BB.mins() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + >>> BB.maxs() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + """ + def __init__(self, data_generator, function, sizes): + r""" + + """ + def gen(size): + return lambda run_id: data_generator(size) + self._bleachermark = Bleachermark([Benchmark([gen(size), function], label=size) for size in sizes]) + + def run(self): + return self._bleachermark.run() + + def timings(self): + r""" + Return all measured timings. + + EXAMPLES:: + + >>> from bleachermark import * + >>> from random import randint + >>> def random_list(n): + ... return [randint(0, n) for i in range(n)] + >>> BB = SimpleBleachermark(random_list, sorted, sizes=[1,2,4,8]) + >>> BB.run() + >>> BB.averages() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + """ + return {size: [t[1] for t in timings] for size,timings in self._bleachermark.timings().items()} + + def averages(self): + """ + Return the averages of the timings + + EXAMPLES:: + >>> from bleachermark import * + >>> from random import randint + >>> def random_list(n): + ... return [randint(0, n) for i in range(n)] + >>> BB = SimpleBleachermark(random_list, sorted, sizes=[1,2,4,8]) + >>> BB.run() + >>> BB.averages() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + """ + return {size: average[1] for size,average in self._bleachermark.averages().items()} + def mins(self): + """ + Return the mins of the timings + + EXAMPLES:: + + >>> from bleachermark import * + >>> from random import randint + >>> def random_list(n): + ... return [randint(0, n) for i in range(n)] + >>> BB = SimpleBleachermark(random_list, sorted, sizes=[1,2,4,8]) + >>> BB.run() + >>> BB.mins() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + """ + return {size: min[1] for size,min in self._bleachermark.mins().items()} + + def maxes(self): + """ + Return the maxes of the timings + + EXAMPLES:: + + >>> from bleachermark import * + >>> from random import randint + >>> def random_list(n): + ... return [randint(0, n) for i in range(n)] + >>> BB = SimpleBleachermark(random_list, sorted, sizes=[1,2,4,8]) + >>> BB.run() + >>> BB.maxes() # random + {1: 4.670000000006613e-06, + 2: 6.58999999999188e-06, + 4: 1.1639999999993878e-05, + 8: 1.8289999999996364e-05} + """ + return {size: max[1] for size,max in self._bleachermark.maxes().items()} #RUNNERS # Runners are essentially iterators that produce the data that the bleachermark will store.