42. timer
— Timer: Measuring elapsed time.¶
Note
Contrary to its use in plain English, we use the word ‘timer’ here for a device that measures the time between two events, not one that counts down a given amount of time.
The Timer class provides a convenient way to measure the time between two events. When developing algorithms for operations on large data models, accurate and multiple measurements of the time spent in different parts of the code are a vital accessory. The Timer class aims at making the process of measuring and reporting as simple is possible for a variety of uses. The following examples illustrate the most common use cases.
Examples
We use time.sleep()
to provide a dummy code block with some known
execution time.
>>> from time import sleep
The example below shows the three basic steps: create a timer, make a measurement, report the result. A Timer instance acts as a context manager, so it can be used in a with statement. The time spent in the block inside the with clause is then measured.
>>> t = Timer() # 1. create a Timer
>>> with t: # 2. measure the time for a code block
... sleep(0.07)
>>> print(t) # 3. report the measured time
Timer: 0.07... sec. Acc: 0.07... sec.
Notice that two values are printed out: the first is the measured time, the second is the accumulated time of all measurements with this timer. Let’s add a second measurement:
>>> sleep(0.05)
>>> with t:
... sleep(0.06)
>>> print(t)
Timer: 0.06... sec. Acc: 0.13... sec.
Notice that only the time inside the with block is measured. Printing out the Timer after measurement is so common, that there is an option to do it automatically. Just set the auto mode on (this can also be given as an option when creating the Timer).
>>> t.auto = True
>>> with t:
... sleep(0.03)
Timer: 0.03... sec. Acc: 0.16... sec.
If you do not need the accumulation feature, you can switch it off by setting acc=None. We create a new Timer t1, with appropriate tag. The tag is the string printed before the timer values (the default tag is ‘Timer’):
>>> with Timer('No accumulation', auto=True, acc=None) as t1:
... sleep(0.07)
No accumulation: 0.07... sec.
>>> with t1: sleep(0.13)
No accumulation: 0.13... sec.
You can specify the precision of the output (the actual readings are still done at the highest precision):
>>> t2 = Timer('Precision 2', dec=2, auto=True)
>>> with t2: sleep(0.057)
Precision 2: 0.06 sec. Acc: 0.06 sec.
>>> with t2: sleep(0.093)
Precision 2: 0.09 sec. Acc: 0.15 sec.
When using multiple timers it is often convenient to be able to use a single
variable to keep all Timer instances alive, or to print all the timers at once.
The Timers
class provides a list of Timer instances to allow that.
>>> timers = Timers(t, t1, t2)
>>> print(timers)
Timer report
Timer: 0.03... sec. Acc: 0.16... sec.
No accumulation: 0.13... sec.
Precision 2: 0.09 sec. Acc: 0.15 sec.
>>> print(len(timers))
3
Plain list methods can be used on the Timers class, but you shouldn’t add
anything but Timer instances. The get()
method retrieves a Timer by its
tag. If multiple timers have the same tag, you can only get the first one.
>>> print(timers.get('No accumulation'))
No accumulation: 0.13... sec.
>>> t3 = Timer('Precision 2')
>>> timers.append(t3)
>>> print(len(timers))
4
>>> timers.get('Precision 2') is t3
False
>>> timers.get('Precision 2') is t2
True
>>> timers.clear()
>>> print(len(timers))
0
Timer context managers can be nested, so one can easily do partial and overall timings at the same time:
>>> with Timer("Outer block", dec=2, reg=timers):
... sleep(0.08)
... with Timer("Inner block", dec=2, reg=timers):
... sleep(0.12)
... sleep(0.07)
... with Timer("Inner block 2", dec=2, reg=timers):
... sleep(0.17)
... sleep(0.06)
...
>>> print(timers)
Timer report
Outer block: 0.50 sec. Acc: 0.50 sec.
Inner block: 0.12 sec. Acc: 0.12 sec.
Inner block 2: 0.17 sec. Acc: 0.17 sec.
The output of the above on some machine gave:
Process time: 0.02... sec. Acc: 0.02... sec.
Real time: 0.08... sec. Acc: 0.08... sec.
There may be times when the use of a context manager is not useful
or even possible (like when the start and end of measurement are in
different functions). Then you can always use the low level interface.
The measurement is always done with the read()
method. It measures
and returns the time since the last start()
or the creation of the
Timer. Here is an example:
>>> t = Timer(dec=2)
>>> sleep(0.03)
>>> v = t.read()
>>> sleep(0.05)
>>> t.start()
>>> sleep(0.07)
>>> v1 = t.read()
>>> print(v, v1)
0.03... 0.07...
The last reading and the accumulated value remain accessible for further processing or customized reports:
>>> print(t2.mem, t2.acc)
0.09... 0.15...
>>> print(f"'{t2.tag}' timer: last read {t2.mem}"
... f", accumulated {t2.acc}")
'Precision 2' timer: last read 0.09, accumulated 0.15
You can have a peek at the current value of the timer, without actually reading it, and thus not chainging the timer’s memory:
>>> print(t)
Timer: 0.07 sec. Acc: 0.10 sec.
>>> sleep(0.09)
>>> print(t.value)
0.09...
>>> print(t)
Timer: 0.07 sec. Acc: 0.10 sec.
Measuring process time is easy as well. Just provide another time measuring function:
>>> timers = Timers(
... Timer('Real time', auto=True, dec=2, acc=None),
... Timer('Process time', auto=True, acc=None, timefunc=time.process_time)
... )
>>> with timers:
... _ = [i*i for i in range(10_000)] # do something
... sleep(0.06)
Process time: 0.00... sec.
Real time: 0.06 sec.
42.1. Classes defined in module timer¶
- class timer.Timer(tag='Timer', *, timefunc=<built-in function perf_counter>, dec=6, acc=0.0, auto=False, reg=None)[source]¶
A class for measuring elapsed time.
The Timer class is a conventient way to measure and report the elapsed time during some processing. It uses Python’s
time.perf_counter()
, providing the highest resolution available. It is however not intended for doing micro measurements: use Python’stimeit
module for that.A Timer instance can be used as a context manager, with automatic reading and even reporting the time spent in the context block. The Timer value is a floating point value giving a number of seconds elapsed since some undefined reference point. By default this is the moment of creation of the Timer, but the starting point can be set at any time.
- Parameters:
tag (str, optional) – A label identifying the Timer. It is shown together with the time value when printing the Timer. It is also used as the key when registering a Timer.
timefunc (callable, optional) – A callable returning a float. The difference in value between two calls is the measured time in seconds. The default
time.perf_counter()
measures real time with high precision. Some other useful values:time.time()
,time.monotonic()
,time.process_time()
,time.thread_time()
.dec (int, optional) – The number of decimals that will be shown when printing the Timer. The default (6) provides microsecond accuracy, which is more than enough for all purposes.
acc (float | None, optional) – Starting value for the accumulator. When a Timer is read, it accumulates the value in its
acc
attribute. The accumulted value is printed out together with the reading. A special value None may be provided to switch off the accumulator.auto (bool, optional) – If True, switches on auto print mode for the Timer. In auto print mode, the value of the Timer is printed out on each
read()
and, when using a context manager, on exiting it. This is very convenient to quickly do timing of some code block. See the examples above. If False, the user is responsible for printing out the Timer.reg (
Timers
, optional) – If aTimers
instance is provided, the newTimer
is added to that collection and its value can be printed out together with that of the others in the collection.
- property value¶
The current value of the Timer.
Peeking at the Timer’s value does not
read()
the Timer.
- property mem¶
The last read value rounded to the timer’s precision.
Note
Use
_mem
to get the full precision.
- property acc¶
The accumulator value rounded to the timer’s precision.
Note
Use
_acc
to get the full precision.
- property tag¶
The timer’s tag string
- newtag(newtag)[source]¶
Change the tag string of the Timer.
- Returns:
self – The Timer itself, thus this method can be used as a context manager.
- class timer.Timers(*timers)[source]¶
A collection of timers
Timers is a
list
ofTimer
instances. All normal list methods are available. Thould take care though to add nothing butTimer
instances to the list.The benefits over using a plain
list
is that printing a Timers will give a nicely formatted output of the timers and that the Timers can be used as a content manager to activate all its Timer instances at once. The latter is e.g. convenient to measure the same code block with different time functions.- Parameters:
*timers (optional) – A sequence of
Timer
instances.
- report(*tags)[source]¶
Return a full report of all existing or requested Timers.
- Parameters:
*tags (sequence, optional) – A sequence of tag strings of the timers that should be included in the report. In none provided, all timers are included.
- Returns:
string – A string containing the formatted Timer instances. This is also the string that will be shown by the print function.