Timer¶
- class delu.tools.Timer[source]¶
Bases:
object
A simple pickle-friendly timer for measuring execution time.
Timer
is applicable to both long-running activies (e.g. a whole program) and limited code regions (e.g. training steps).Timer
can be paused/resumed to measure execution time of only relevant activities.Timer
can be used as a context manager.Timer
is pickle-friendly and can be saved to / loaded from a checkpoint.Timer
can report the elapsed time as a human-readable string withprint
,str
,format
and f-strings.
Note
Under the hood,
Timer
usestime.perf_counter
to perform time measurements.Timer
is not aware of CUDA, so things liketorch.cuda.synchronize
must be called explicitly if needed.
Usage
The common setup for all examples:
>>> from time import sleep >>> >>> def train_epoch(): sleep(0.001) >>> def evaluate_epoch(): sleep(0.001)
Measuring the execution time of a training loop:
>>> # Initially, the timer is not running. >>> timer = delu.tools.Timer() >>> assert not timer.is_running >>> >>> # Run the timer. >>> timer.run() >>> assert timer.is_running >>> >>> for epoch in range(100): ... train_epoch() ... evaluate_epoch() >>> duration = timer.elapsed()
Same as above, but using a context:
Important
When entering the context, the timer must not be running.
>>> # Or simply `with delu.tools.Timer() as timer:` >>> timer = delu.tools.Timer() >>> with timer: ... # On enter, timer.run() is called. ... assert timer.is_running ... for epoch in range(100): ... train_epoch() ... evaluate_epoch() ... >>> # On exit, timer.pause() is called. >>> assert not timer.is_running >>> duration = timer.elapsed()
Measuring the execution time only of the training activity:
>>> timer = delu.tools.Timer() >>> for epoch in range(100): ... timer.run() # Start/resume the timer. ... train_epoch() # Recorded. ... timer.pause() # Pause the timer. ... ... evaluate_epoch() # Not recorded. >>> total_training_duration = timer.elapsed()
Same as above, but using a context:
>>> timer = delu.tools.Timer() >>> for epoch in range(100): ... with timer: ... train_epoch() ... evaluate_epoch() >>> total_training_duration = timer.elapsed()
Using the timer as a global timer (the difference with using
time.perf_counter
directly is that thestart
variable below can be safely saved to / loaded from a checkpoint):>>> timer = delu.tools.Timer() >>> timer.run() >>> ... # Other activities. >>> start = timer.elapsed() >>> sleep(0.001) >>> end = timer.elapsed() >>> duration = end - start
Resetting the timer:
>>> timer.reset() >>> assert not timer.is_running >>> timer.elapsed() == 0.0 True
Reporting the elapsed time in a human-readable format (the default format is the same as for
datetime.timedelta
:[days "days" if >0] hours:minutes:seconds[.microseconds if >0]
):>>> timer = delu.tools.Timer() >>> timer.run() >>> sleep(2.0) # Sleep for two seconds. >>> timer.pause() >>> >>> # print(timer) also works >>> str(timer) == format(timer) == f'{timer}' >>> >>> # Two seconds plus epsilon: >>> str(timer).startswith('0:00:02.') True >>> f'{timer:%Hh %Mm %Ss}' '00h 00m 02s' >>> format(timer, '%Hh %Mm %Ss') '00h 00m 02s'
A timer can be saved with
torch.save
/pickle.dump
and loaded withtorch.load
/pickle.load
together with other objects:>>> import tempfile >>> >>> model = nn.Linear(1, 1) >>> timer = delu.tools.Timer() >>> timer.run() >>> sleep(0.001) >>> ... >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = f'{tmpdir}/checkpoint.pt' ... torch.save( ... {'model': model.state_dict(), 'timer': timer}, ... path, ... ) ... ... ... checkpoint = torch.load(path) ... model.load_state_dict(checkpoint['model']) ... # The just loaded timer is on pause, ... # so it must be explicitly resumed. ... timer = checkpoint['timer'] ... assert not timer.is_running ... timer.run()
Additional technical examples:
>>> # Implementing a pause context. >>> from contextlib import ExitStack >>> >>> timer = delu.tools.Timer() >>> timer.run() >>> ... >>> timer.pause() >>> with ExitStack() as stack: ... # Call `timer.run()` on exit. ... stack.callback(timer.run) ... ... # Some activity which is not recorded by the timer. >>> timer.is_running True
- __enter__() Timer [source]¶
Measure time within a context.
The timer must not be running when entering the context.
On enter,
Timer.run
is called regardless of the current state.On exit,
Timer.pause
is called regardless of the current state.
- __format__(format_spec: str, /) str [source]¶
Format the time elapsed since the start in a human-readable string.
This is a shortcut for
time.strftime(format_str, time.gmtime(self()))
.- Parameters:
format_str – the format string passed to
time.strftime
.- Returns:
the filled
format_str
.
Usage
>>> # xdoctest: +SKIP >>> timer = delu.tools.Timer() >>> # Let's say that exactly 3661 seconds have passed. >>> assert format(timer, '%Hh %Mm %Ss') == '01h 01m 01s'
- __setstate__(state: dict[str, Any]) None [source]¶
Load the state.
A time with just loaded state is not running (basically, it is a freshly created timer which stores the elapsed time from the loaded state).
- elapsed() float [source]¶
Get the time elapsed.
The elapsed time is the time (in seconds) passed since the first
.run()
call up to the.elapsed()
call, minus pauses.- Returns:
The elapsed time.
- pause() None [source]¶
Pause the timer.
If the timer is running, the method pauses the timer. If the timer was never
.run()
or is already on pause, the method does nothing.