-
Notifications
You must be signed in to change notification settings - Fork 0
04_Time
The nob.time module provides utilities for measuring the duration of code blocks and for forcing the throughput of loops. These utilities are designed on top of the nob.human module.
The time.about function behaves in 3 modes.
You can simply wrap your code block with time.about to measure its duration. Get the duration in seconds with t.duration or in a human-readable format with t.duration_human.
from time import sleep
from nob import time
with time.about() as t:
sleep(1)
print(f"Elapsed time: {t.duration_human}.")You can also pass a function and its arguments to time.about to measure the duration of the function call. The result of the function call will be stored in t.result. The arguments can be passed as a mix of positional and keyword arguments.
from time import sleep
from nob import time
def my_func(x: int, y: int):
sleep(1)
return x + y
res = time.about(my_func, 1, y=2)
print(f"Result: {res.result}, Time taken: {res.duration_human}.")If you pass an iterator to time.about, it will exhaust the iterator and measure the time taken to do so. The number of items exhausted will be stored in t.count, and a human-readable version of it in t.count_human. The throughput (items per second) will be stored in t.throughput, and a human-readable version of it in t.throughput_human.
from time import sleep
from nob import time
it = time.about(range(100))
for _ in it:
sleep(0.01)
print(f"Exhausted {it.count_human} items in {it.duration_human} (at {it.throughput_human}).")The time.tick function allows you to force the throughput of a loop. It will automatically sleep for the right amount of time between items to achieve the desired throughput. The throughput should be specified in items per second. You can optionally tell the function to average over any period of time using the mean_over parameter (in seconds).
You can wrap your iterator with time.about.
from nob import time
it = time.about(range(100))
for _ in time.tick(it):
pass
print(f"Exhausted {it.count_human} items in {it.duration_human} (at {it.throughput_human}).")This code block should be a lot closer to 1 second than the previous example!
The time.tick function can be called without an iterator as well. In this case, it will simply sleep for the right amount of time between calls to achieve the desired throughput.
from nob import time
it = time.about(range(100))
for _ in it:
time.tick()
print(f"Exhausted {it.count_human} items in {it.duration_human} (at {it.throughput_human}).")Both mode of operation should be equivalent.
However, not supplying an iterator to time.tick can be harmful since we have to rely on sys._getframe(1) to create new instances of the internal time.TickRateCounter, because we don't wan't multiple independent loops affecting each other's throughput. There are some edge cases which can cause this to break.
Tip
Pass your own time.TickRateCounter instances (if you know when to create new ones) using the safe_counter parameter to time.tick to avoid the potential issues. Call .reset() to clear all registered frames.
Because why not.
from nob import progress, time
it = time.about(time.tick(progress.track(range(100))))
for _ in it:
pass
print(f"Exhausted {it.count_human} items in {it.duration_human} (at {it.throughput_human}).")Powered by caffeine and uv.
MIT license.