Profiling regions#
A region is the fundamental unit of measurement in scope-profiler. Every time a region is entered and exited, the profiler records a start/end timestamp pair and increments the call counter.
Creating regions#
Decorator — @ProfileManager.profile#
Ideal for wrapping entire functions:
from scope_profiler import ProfileManager
@ProfileManager.profile("solver")
def solve(A, b):
return np.linalg.solve(A, b)
Without an explicit name the function name is used:
@ProfileManager.profile
def solve(A, b):
return np.linalg.solve(A, b)
# region name: "solve"
Context manager — ProfileManager.profile_region()#
Ideal for profiling a section of code inside a function:
def time_step(state, dt):
with ProfileManager.profile_region("rhs_evaluation"):
rhs = compute_rhs(state)
with ProfileManager.profile_region("state_update"):
state += dt * rhs
Mixing both#
Decorators and context managers can be mixed freely. They share the same region registry, so using the same name in both places accumulates into a single region:
@ProfileManager.profile("compute")
def compute_batch(data):
for chunk in data:
with ProfileManager.profile_region("compute"):
process(chunk)
# Both the decorator and the context manager contribute
# to the same "compute" region.
Region identity#
Regions are identified by name (a string). The first call to
profile_region("foo") creates the region; subsequent calls with the
same name return the existing instance. This means:
The same region can be entered from multiple call sites.
Call counts and timestamps accumulate across all sites.
Regions are scoped to the current
ProfileManager.setup()session. Callingsetup()again clears all regions.
Nesting#
Regions can be nested arbitrarily:
with ProfileManager.profile_region("outer"):
with ProfileManager.profile_region("inner"):
work()
Each region independently records its own timestamps. The library does not compute parent-child relationships — that can be done in post-processing by comparing the recorded intervals.
Accessing region data at runtime#
Before calling finalize(), you can inspect in-memory data:
region = ProfileManager.get_region("solver")
# Number of completed calls
region.num_calls
# Durations of buffered (not yet flushed) calls, in nanoseconds
durations = region.get_durations_numpy()
All registered regions:
for name, region in ProfileManager.get_all_regions().items():
print(f"{name}: {region.num_calls} calls")