Categories¶
A category is any string you attach to a section to group it with others, such as "io",
"gpu", "db" or "cpu". Sections without one get
DEFAULT_CATEGORY, "default".
import time
from execution_timer import TimerContext, get_execution_timings, get_total_category_time
with TimerContext("train"):
with TimerContext("load_batch", category="io"):
time.sleep(0.02)
with TimerContext("forward", category="gpu"):
time.sleep(0.03)
with TimerContext("save_checkpoint", category="io"):
time.sleep(0.01)
timings = get_execution_timings()
io_sections = timings["train", "load_batch"]["time"] + timings["train", "save_checkpoint"]["time"]
assert get_total_category_time("io") == io_sections
How category totals are counted¶
get_total_category_time() adds up the top-most section of the
category along each branch. A section nested inside another section of the same category
is already part of that section’s time, so it isn’t counted again:
from execution_timer import clear_execution_timings
clear_execution_timings()
with TimerContext("query", category="db"):
time.sleep(0.01)
with TimerContext("fetch_rows", category="db"): # already inside "query"
time.sleep(0.02)
# Only "query" counts; "fetch_rows" is already part of its time.
assert get_total_category_time("db") == get_execution_timings()[("query",)]["time"]
Sections of other categories in between don’t change this. A db section inside an io
section inside another db section still counts only once.
A category with no sections totals 0.0. The JSON export includes every category’s total
in one go; see Reports and export.
One category per section¶
Repeated entries of the same section add to one total. If you enter the same path with a different category, the latest category applies to the section’s whole accumulated time. Keep one category per section path if you need the totals to stay apart.
Forbidding a nesting¶
Some combinations are mistakes, for example CPU-side work inside a section meant to
measure pure GPU time. register_forbidden_nesting() turns them into
an error at the moment they happen:
from execution_timer import clear_forbidden_nesting, register_forbidden_nesting
register_forbidden_nesting(outer="gpu", inner="cpu")
try:
with TimerContext("kernel", category="gpu"):
with TimerContext("reduce", category="cpu"):
pass
except ValueError as error:
print(error)
Category 'cpu' is not allowed inside category 'gpu'.
Entering the inner section raises ValueError, and the inner section isn’t
recorded. Only the direct parent is checked: a cpu section inside an io section inside
a gpu section is allowed.
Rules apply to the whole process and persist until you call
clear_forbidden_nesting(). Clearing the timings leaves the rules in
place.
clear_forbidden_nesting()