1

Sorry for the possible duplication of a question. Unfortunately, I could not find an answer that is convenient for me. I want to write a class to calculate the execution time (the mean and the rms of execution time) of a code in the manner of %timeit in ipython. For this, I used this code https://stackoverflow.com/a/28218696/5328802 to use in with statement.

Here is the test:

from TimingManager import TimingManager
import numpy as np

N = 10000

def myfunction1():
    l = [i**2 for i in range(N)]
def myfunction2():
    l = np.arange(N)**2

print("List test")
with TimingManager(fun=yourfunction1,repeat=10) as t:
    t.start()

print("Array test")
with TimingManager(fun=yourfunction2, repeat=10) as t:
    t.start()

And here is my realisation of TimingManager class (file TimingManager.py):

import timeit
import statistics

class TimingManager():
    """Context Manager used with the statement 'with' to time some execution.

    Example:

    from TimingManager import TimingManager
    with TimingManager(fun=yourfunction,repeat=10) as t:
       t.start()
    """
    clock = timeit.default_timer

    def __init__(self,fun,repeat=10):
        self.repeat = repeat
        self.time_table = []
        self.run = fun

    def start(self):
        for i in range(self.repeat):
            self.timestart = self.clock()
            self.run()
            self.time_table.append(self.clock() - self.timestart)

    def __enter__(self):
        """ action on start """
        self.timestart = self.clock()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """ action on exit """
        self.print_process_stat()

        return False

    def print_process_stat(self):
        tc = statistics.mean(self.time_table)
        dt = statistics.pstdev(self.time_table)
        print("Execution time is %s ± %s"%(time_conversion(tc),time_conversion(dt)))

def time_conversion(t):
    if t < 1e-9:
        return "%4.3g ps" % (t/1e-12)
    elif t < 1e-6:
        return "%4.3g ms" % (t/1e-9)
    elif t < 1e-3:
        return "%4.3g μs" % (t/1e-6)
    elif t < 1:
        return "%4.3g ms" % (t/1e-3)
    elif t < 60:
        return "%4.3g s" % (t)
    elif t < 3600:
        return "%2d min %2d s" % (t//60,t%60)
    elif t < 24*3600:
        return "%2d h %2d min" % (t//3600,(t/60)%60)
    else:
        return "%2d d %2d h" % (t//(24*3600),(t/60)%60)

It gives me statistics on the calculation time of what I need. But in my opinion this is not elegant. So my question is, is it possible to implement this procedure in a more elegant way (without defining the myfunction), namely to access the block after the with statement (perhaps using the content manager) to repeat it inside the TimingManager class? To use finally something like this:

with TimingManager(repeat=10):
    Statemet1
    Statemet2
    ..
Alexander Korovin
  • 1,639
  • 1
  • 12
  • 19
  • 1
    Context managers don't get to control the `with` body. They do three things: run code on entry, run code on exit, and optionally suppress exceptions. This isn't like Ruby's `yield`. – user2357112 Nov 12 '20 at 22:14
  • 1
    Instead you could use a function decorator which can run the function body more than once. – Dan D. Nov 12 '20 at 22:16
  • Thank you https://stackoverflow.com/users/2357112/user2357112-supports-monica and https://stackoverflow.com/users/388787/dan-d. But for function decoration I need to define `myfunction` again. I want to find a solution without this definition. – Alexander Korovin Nov 12 '20 at 22:22
  • @AlexanderKorovin it is unclear to me how you want to avoid defining `myfunction`. This is what you want to time, no? How will you know what to time? – Mickey Nov 12 '20 at 22:36
  • @Mickey, this is my question. Here I can repeat the statement block in `myfunction`, but is it possible to get the context of the with block and repeat it in the `TimingManager` class. Unfortunately, I don't know how to do this, or is it possible at all. – Alexander Korovin Nov 12 '20 at 22:49

0 Answers0