Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
timeit.py379 linesDownload Raw Back to Lib
1"""Tool for measuring execution time of small code snippets.2 3This module avoids a number of common traps for measuring execution4times.  See also Tim Peters' introduction to the Algorithms chapter in5the Python Cookbook, published by O'Reilly.6 7Library usage: see the Timer class.8 9Command line usage:10    python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [--] [statement]11 12Options:13  -n/--number N: how many times to execute 'statement' (default: see below)14  -r/--repeat N: how many times to repeat the timer (default 5)15  -s/--setup S: statement to be executed once initially (default 'pass').16                Execution time of this setup statement is NOT timed.17  -p/--process: use time.process_time() (default is time.perf_counter())18  -v/--verbose: print raw timing results; repeat for more digits precision19  -u/--unit: set the output time unit (nsec, usec, msec, or sec)20  -h/--help: print this usage message and exit21  --: separate options from statement, use when statement starts with -22  statement: statement to be timed (default 'pass')23 24A multi-line statement may be given by specifying each line as a25separate argument; indented lines are possible by enclosing an26argument in quotes and using leading spaces.  Multiple -s options are27treated similarly.28 29If -n is not given, a suitable number of loops is calculated by trying30increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the31total time is at least 0.2 seconds.32 33Note: there is a certain baseline overhead associated with executing a34pass statement.  It differs between versions.  The code here doesn't try35to hide it, but you should be aware of it.  The baseline overhead can be36measured by invoking the program without arguments.37 38Classes:39 40    Timer41 42Functions:43 44    timeit(string, string) -> float45    repeat(string, string) -> list46    default_timer() -> float47"""48 49import gc50import itertools51import sys52import time53 54__all__ = ["Timer", "timeit", "repeat", "default_timer"]55 56dummy_src_name = "<timeit-src>"57default_number = 100000058default_repeat = 559default_timer = time.perf_counter60 61_globals = globals62 63# Don't change the indentation of the template; the reindent() calls64# in Timer.__init__() depend on setup being indented 4 spaces and stmt65# being indented 8 spaces.66template = """67def inner(_it, _timer{init}):68    {setup}69    _t0 = _timer()70    for _i in _it:71        {stmt}72        pass73    _t1 = _timer()74    return _t1 - _t075"""76 77 78def reindent(src, indent):79    """Helper to reindent a multi-line statement."""80    return src.replace("\n", "\n" + " " * indent)81 82 83class Timer:84    """Class for timing execution speed of small code snippets.85 86    The constructor takes a statement to be timed, an additional87    statement used for setup, and a timer function.  Both statements88    default to 'pass'; the timer function is platform-dependent (see89    module doc string).  If 'globals' is specified, the code will be90    executed within that namespace (as opposed to inside timeit's91    namespace).92 93    To measure the execution time of the first statement, use the94    timeit() method.  The repeat() method is a convenience to call95    timeit() multiple times and return a list of results.96 97    The statements may contain newlines, as long as they don't contain98    multi-line string literals.99    """100 101    def __init__(self, stmt="pass", setup="pass", timer=default_timer,102                 globals=None):103        """Constructor.  See class doc string."""104        self.timer = timer105        local_ns = {}106        global_ns = _globals() if globals is None else globals107        init = ''108        if isinstance(setup, str):109            # Check that the code can be compiled outside a function110            compile(setup, dummy_src_name, "exec")111            stmtprefix = setup + '\n'112            setup = reindent(setup, 4)113        elif callable(setup):114            local_ns['_setup'] = setup115            init += ', _setup=_setup'116            stmtprefix = ''117            setup = '_setup()'118        else:119            raise ValueError("setup is neither a string nor callable")120        if isinstance(stmt, str):121            # Check that the code can be compiled outside a function122            compile(stmtprefix + stmt, dummy_src_name, "exec")123            stmt = reindent(stmt, 8)124        elif callable(stmt):125            local_ns['_stmt'] = stmt126            init += ', _stmt=_stmt'127            stmt = '_stmt()'128        else:129            raise ValueError("stmt is neither a string nor callable")130        src = template.format(stmt=stmt, setup=setup, init=init)131        self.src = src  # Save for traceback display132        code = compile(src, dummy_src_name, "exec")133        exec(code, global_ns, local_ns)134        self.inner = local_ns["inner"]135 136    def print_exc(self, file=None):137        """Helper to print a traceback from the timed code.138 139        Typical use:140 141            t = Timer(...)       # outside the try/except142            try:143                t.timeit(...)    # or t.repeat(...)144            except:145                t.print_exc()146 147        The advantage over the standard traceback is that source lines148        in the compiled template will be displayed.149 150        The optional file argument directs where the traceback is151        sent; it defaults to sys.stderr.152        """153        import linecache, traceback154        if self.src is not None:155            linecache.cache[dummy_src_name] = (len(self.src),156                                               None,157                                               self.src.split("\n"),158                                               dummy_src_name)159        # else the source is already stored somewhere else160 161        traceback.print_exc(file=file)162 163    def timeit(self, number=default_number):164        """Time 'number' executions of the main statement.165 166        To be precise, this executes the setup statement once, and167        then returns the time it takes to execute the main statement168        a number of times, as float seconds if using the default timer.   The169        argument is the number of times through the loop, defaulting170        to one million.  The main statement, the setup statement and171        the timer function to be used are passed to the constructor.172        """173        it = itertools.repeat(None, number)174        gcold = gc.isenabled()175        gc.disable()176        try:177            timing = self.inner(it, self.timer)178        finally:179            if gcold:180                gc.enable()181        return timing182 183    def repeat(self, repeat=default_repeat, number=default_number):184        """Call timeit() a few times.185 186        This is a convenience function that calls the timeit()187        repeatedly, returning a list of results.  The first argument188        specifies how many times to call timeit(), defaulting to 5;189        the second argument specifies the timer argument, defaulting190        to one million.191 192        Note: it's tempting to calculate mean and standard deviation193        from the result vector and report these.  However, this is not194        very useful.  In a typical case, the lowest value gives a195        lower bound for how fast your machine can run the given code196        snippet; higher values in the result vector are typically not197        caused by variability in Python's speed, but by other198        processes interfering with your timing accuracy.  So the min()199        of the result is probably the only number you should be200        interested in.  After that, you should look at the entire201        vector and apply common sense rather than statistics.202        """203        r = []204        for i in range(repeat):205            t = self.timeit(number)206            r.append(t)207        return r208 209    def autorange(self, callback=None):210        """Return the number of loops and time taken so that total time >= 0.2.211 212        Calls the timeit method with increasing numbers from the sequence213        1, 2, 5, 10, 20, 50, ... until the time taken is at least 0.2214        second.  Returns (number, time_taken).215 216        If *callback* is given and is not None, it will be called after217        each trial with two arguments: ``callback(number, time_taken)``.218        """219        i = 1220        while True:221            for j in 1, 2, 5:222                number = i * j223                time_taken = self.timeit(number)224                if callback:225                    callback(number, time_taken)226                if time_taken >= 0.2:227                    return (number, time_taken)228            i *= 10229 230 231def timeit(stmt="pass", setup="pass", timer=default_timer,232           number=default_number, globals=None):233    """Convenience function to create Timer object and call timeit method."""234    return Timer(stmt, setup, timer, globals).timeit(number)235 236 237def repeat(stmt="pass", setup="pass", timer=default_timer,238           repeat=default_repeat, number=default_number, globals=None):239    """Convenience function to create Timer object and call repeat method."""240    return Timer(stmt, setup, timer, globals).repeat(repeat, number)241 242 243def main(args=None, *, _wrap_timer=None):244    """Main program, used when run as a script.245 246    The optional 'args' argument specifies the command line to be parsed,247    defaulting to sys.argv[1:].248 249    The return value is an exit code to be passed to sys.exit(); it250    may be None to indicate success.251 252    When an exception happens during timing, a traceback is printed to253    stderr and the return value is 1.  Exceptions at other times254    (including the template compilation) are not caught.255 256    '_wrap_timer' is an internal interface used for unit testing.  If it257    is not None, it must be a callable that accepts a timer function258    and returns another timer function (used for unit testing).259    """260    if args is None:261        args = sys.argv[1:]262    import getopt263    try:264        opts, args = getopt.getopt(args, "n:u:s:r:pvh",265                                   ["number=", "setup=", "repeat=",266                                    "process", "verbose", "unit=", "help"])267    except getopt.error as err:268        print(err)269        print("use -h/--help for command line help")270        return 2271 272    timer = default_timer273    stmt = "\n".join(args) or "pass"274    number = 0  # auto-determine275    setup = []276    repeat = default_repeat277    verbose = 0278    time_unit = None279    units = {"nsec": 1e-9, "usec": 1e-6, "msec": 1e-3, "sec": 1.0}280    precision = 3281    for o, a in opts:282        if o in ("-n", "--number"):283            number = int(a)284        if o in ("-s", "--setup"):285            setup.append(a)286        if o in ("-u", "--unit"):287            if a in units:288                time_unit = a289            else:290                print("Unrecognized unit. Please select nsec, usec, msec, or sec.",291                      file=sys.stderr)292                return 2293        if o in ("-r", "--repeat"):294            repeat = int(a)295            if repeat <= 0:296                repeat = 1297        if o in ("-p", "--process"):298            timer = time.process_time299        if o in ("-v", "--verbose"):300            if verbose:301                precision += 1302            verbose += 1303        if o in ("-h", "--help"):304            print(__doc__, end="")305            return 0306    setup = "\n".join(setup) or "pass"307 308    # Include the current directory, so that local imports work (sys.path309    # contains the directory of this script, rather than the current310    # directory)311    import os312    sys.path.insert(0, os.curdir)313    if _wrap_timer is not None:314        timer = _wrap_timer(timer)315 316    t = Timer(stmt, setup, timer)317    if number == 0:318        # determine number so that 0.2 <= total time < 2.0319        callback = None320        if verbose:321            def callback(number, time_taken):322                msg = "{num} loop{s} -> {secs:.{prec}g} secs"323                plural = (number != 1)324                print(msg.format(num=number, s='s' if plural else '',325                                 secs=time_taken, prec=precision))326        try:327            number, _ = t.autorange(callback)328        except:329            t.print_exc()330            return 1331 332        if verbose:333            print()334 335    try:336        raw_timings = t.repeat(repeat, number)337    except:338        t.print_exc()339        return 1340 341    def format_time(dt):342        unit = time_unit343 344        if unit is not None:345            scale = units[unit]346        else:347            scales = [(scale, unit) for unit, scale in units.items()]348            scales.sort(reverse=True)349            for scale, unit in scales:350                if dt >= scale:351                    break352 353        return "%.*g %s" % (precision, dt / scale, unit)354 355    if verbose:356        print("raw times: %s" % ", ".join(map(format_time, raw_timings)))357        print()358    timings = [dt / number for dt in raw_timings]359 360    best = min(timings)361    print("%d loop%s, best of %d: %s per loop"362          % (number, 's' if number != 1 else '',363             repeat, format_time(best)))364 365    best = min(timings)366    worst = max(timings)367    if worst >= best * 4:368        import warnings369        warnings.warn_explicit("The test results are likely unreliable. "370                               "The worst time (%s) was more than four times "371                               "slower than the best time (%s)."372                               % (format_time(worst), format_time(best)),373                               UserWarning, '', 0)374    return None375 376 377if __name__ == "__main__":378    sys.exit(main())379 
codekingpro/portable-devtools · Team Ai