Skip to main content
`Ctrl`+`K`

#

# Source code for ase.utils.timing

    # Copyright (C) 2003  CAMP
    # Please see the accompanying LICENSE file for further information.

    import functools
    import inspect
    import sys
    import time

    def function_timer(func, *args, **kwargs):
        out = kwargs.pop('timeout', sys.stdout)
        t1 = time.time()
        r = func(*args, **kwargs)
        t2 = time.time()
        print(t2 - t1, file=out)
        return r

    class Timer:
        """Timer object.

        Use like this::

            timer = Timer()
            timer.start('description')
            # do something
            timer.stop()

        or::

            with timer('description'):
                # do something

        To get a summary call::

            timer.write()

        """

        def __init__(self, print_levels=1000):
            self.timers = {}
            self.t0 = time.time()
            self.running = []
            self.print_levels = print_levels

        def print_info(self, calc):
            """Override to get to write info during calculator's initialize()."""

        def start(self, name):
            names = tuple(self.running + [name])
            self.timers[names] = self.timers.get(names, 0.0) - time.time()
            self.running.append(name)

        def stop(self, name=None):
            if name is None:
                name = self.running[-1]
            names = tuple(self.running)
            running = self.running.pop()
            if name != running:
                raise RuntimeError(
                    'Must stop timers by stack order.  '
                    'Requested stopping of %s but topmost is %s' % (name, running)
                )
            self.timers[names] += time.time()
            return names

        def __call__(self, name):
            """Context manager for timing a block of code.

            Example (t is a timer object)::

                with t('Add two numbers'):
                    x = 2 + 2

                # same as this:
                t.start('Add two numbers')
                x = 2 + 2
                t.stop()
            """
            self.start(name)
            return self

        def __enter__(self):
            pass

        def __exit__(self, *args):
            self.stop()

        def get_time(self, *names):
            return self.timers[names]

        def write(self, out=sys.stdout):
            were_running = list(self.running)
            while self.running:
                self.stop()
            if len(self.timers) == 0:
                return

            t0 = time.time()
            tot = t0 - self.t0

            n = max(len(names[-1]) + len(names) for names in self.timers) + 1
            line = '-' * (n + 26) + '\n'
            out.write('%-*s    incl.     excl.\n' % (n, 'Timing:'))
            out.write(line)
            tother = tot

            inclusive = self.timers.copy()
            exclusive = self.timers.copy()
            keys = sorted(exclusive.keys())
            for names in keys:
                t = exclusive[names]
                if len(names) > 1:
                    if len(names) < self.print_levels + 1:
                        exclusive[names[:-1]] -= t
                else:
                    tother -= t
            exclusive[('Other',)] = tother
            inclusive[('Other',)] = tother
            keys.append(('Other',))
            for names in keys:
                t = exclusive[names]
                tinclusive = inclusive[names]
                r = t / tot
                p = 100 * r
                i = int(40 * r + 0.5)
                if i == 0:
                    bar = '|'
                else:
                    bar = '|%s|' % ('-' * (i - 1))
                level = len(names)
                if level > self.print_levels:
                    continue
                name = (level - 1) * ' ' + names[-1] + ':'
                out.write(
                    '%-*s%9.3f %9.3f %5.1f%% %s\n'
                    % (n, name, tinclusive, t, p, bar)
                )
            out.write(line)
            out.write('%-*s%9.3f %5.1f%%\n\n' % (n + 10, 'Total:', tot, 100.0))

            for name in were_running:
                self.start(name)

        def add(self, timer):
            for name, t in timer.timers.items():
                self.timers[name] = self.timers.get(name, 0.0) + t

    class timer:
        """Decorator for timing a method call.

        Example::

            from ase.utils.timing import timer, Timer

            class A:
                def __init__(self):
                    self.timer = Timer()

                @timer('Add two numbers')
                def add(self, x, y):
                    return x + y

        """

        def __init__(self, name):
            self.name = name

        def __call__(self, method):
            if inspect.isgeneratorfunction(method):

                @functools.wraps(method)
                def new_method(slf, *args, **kwargs):
                    gen = method(slf, *args, **kwargs)
                    while True:
                        slf.timer.start(self.name)
                        try:
                            x = next(gen)
                        except StopIteration:
                            break
                        finally:
                            slf.timer.stop()
                        yield x
            else:

                @functools.wraps(method)
                def new_method(slf, *args, **kwargs):
                    slf.timer.start(self.name)
                    x = method(slf, *args, **kwargs)
                    try:
                        slf.timer.stop()
                    except IndexError:
                        pass
                    return x

            return new_method