diff -r 90681462f16a Lib/timeit.py --- a/Lib/timeit.py Wed Sep 21 14:14:59 2016 +0200 +++ b/Lib/timeit.py Wed Sep 21 15:59:40 2016 +0200 @@ -50,7 +50,6 @@ Functions: """ -import gc import sys import time import itertools @@ -59,7 +58,7 @@ import itertools dummy_src_name = "" default_number = 1000000 -default_repeat = 3 +default_repeat = 5 default_timer = time.perf_counter _globals = globals @@ -172,13 +171,7 @@ class Timer: the timer function to be used are passed to the constructor. """ it = itertools.repeat(None, number) - gcold = gc.isenabled() - gc.disable() - try: - timing = self.inner(it, self.timer) - finally: - if gcold: - gc.enable() + timing = self.inner(it, self.timer) return timing def repeat(self, repeat=default_repeat, number=default_number): @@ -218,7 +211,7 @@ class Timer: If *callback* is given and is not None, it will be called after each trial with two arguments: ``callback(number, time_taken)``. """ - for i in range(1, 10): + for i in range(0, 10): number = 10**i time_taken = self.timeit(number) if callback: @@ -237,6 +230,23 @@ def repeat(stmt="pass", setup="pass", ti """Convenience function to create Timer object and call repeat method.""" return Timer(stmt, setup, timer, globals).repeat(repeat, number) + +def _format_loops(number): + r = 1 + if number >= 10_000: + x = number + pow10 = 0 + while x >= 10: + x, r = divmod(x, 10) + pow10 += 1 + if r: + break + + if not r: + return '10^%s' % pow10 + else: + return str(number) + def main(args=None, *, _wrap_timer=None): """Main program, used when run as a script. @@ -256,7 +266,10 @@ def main(args=None, *, _wrap_timer=None) """ if args is None: args = sys.argv[1:] + import getopt + import statistics + try: opts, args = getopt.getopt(args, "n:u:s:r:tcpvh", ["number=", "setup=", "repeat=", @@ -266,6 +279,7 @@ def main(args=None, *, _wrap_timer=None) print(err) print("use -h/--help for command line help") return 2 + timer = default_timer stmt = "\n".join(args) or "pass" number = 0 # auto-determine @@ -273,19 +287,26 @@ def main(args=None, *, _wrap_timer=None) repeat = default_repeat verbose = 0 time_unit = None - units = {"usec": 1, "msec": 1e3, "sec": 1e6} + units = {"sec": 1, "msec": 1e3, "usec": 1e6, "ns": 1e9} precision = 3 for o, a in opts: if o in ("-n", "--number"): - number = int(a) + if '^' in a: + number, _, power = a.partition('^') + number = int(number) + power = int(power) + number = number ** power + else: + number = int(a) if o in ("-s", "--setup"): setup.append(a) if o in ("-u", "--unit"): if a in units: time_unit = a else: - print("Unrecognized unit. Please select usec, msec, or sec.", - file=sys.stderr) + print("Unrecognized unit. Please select: %s" + % ', '.join(units), + file=sys.stderr) return 2 if o in ("-r", "--repeat"): repeat = int(a) @@ -304,6 +325,7 @@ def main(args=None, *, _wrap_timer=None) if o in ("-h", "--help"): print(__doc__, end=' ') return 0 + setup = "\n".join(setup) or "pass" # Include the current directory, so that local imports work (sys.path # contains the directory of this script, rather than the current @@ -319,43 +341,49 @@ def main(args=None, *, _wrap_timer=None) if verbose: def callback(number, time_taken): msg = "{num} loops -> {secs:.{prec}g} secs" - print(msg.format(num=number, secs=time_taken, prec=precision)) + print(msg.format(num=_format_loops(number), + secs=time_taken, prec=precision)) try: number, _ = t.autorange(callback) except: t.print_exc() return 1 + try: - r = t.repeat(repeat, number) + timings = t.repeat(repeat, number) except: t.print_exc() return 1 - best = min(r) + if verbose: - print("raw times:", " ".join(["%.*g" % (precision, x) for x in r])) - print("%d loops," % number, end=' ') - usec = best * 1e6 / number + raw_times = ", ".join("%.*g sec" % (precision, x) for x in timings) + print("raw times: %s" % raw_times) + + timings = [dt / number for dt in timings] + + average = sum(timings) / len(timings) if time_unit is not None: scale = units[time_unit] else: scales = [(scale, unit) for unit, scale in units.items()] - scales.sort(reverse=True) + scales.sort() for scale, time_unit in scales: - if usec >= scale: + if average * scale >= 1.0: break - print("best of %d: %.*g %s per loop" % (repeat, precision, - usec/scale, time_unit)) - best = min(r) - usec = best * 1e6 / number - worst = max(r) + + stdev = statistics.stdev(timings, average) + print("%s loops, average of %d: %.*g %s +- %.*g %s per loop" + % (_format_loops(number), repeat, + precision, average * scale, time_unit, + precision, stdev * scale, time_unit)) + best = min(timings) + worst = max(timings) if worst >= best * 4: - usec = worst * 1e6 / number - import warnings - warnings.warn_explicit( - "The test results are likely unreliable. The worst\n" - "time (%.*g %s) was more than four times slower than the best time." % - (precision, usec/scale, time_unit), - UserWarning, '', 0) + print("WARNING: The test results are likely unreliable. " + "The worst time (%.*g %s) was more than four times slower than " + "the best time (%.*g %s)." + % (precision, worst * scale, time_unit, + precision, best * scale, time_unit)) return None if __name__ == "__main__":