Skip to content
Projects

scope_profiler.region_profiler

Profile region classes implementing the strategy pattern for different profiling modes.

Classes

NameDescription
AggregateProfileRegionLow-memory region that records aggregate timing statistics only.
BaseProfileRegionBase class providing shared profiling logic.
CUDATimingNVTXProfileRegionRegion that records CPU/CUDA-event timing and emits an NVTX range.
CUDATimingProfileRegionRegion that records CPU timing plus CUDA-event elapsed device time.
DisabledProfileRegionProfiling region that performs no measurements.
FullProfileRegionRegion that records both timing and LIKWID metrics, and writes to HDF5.
LineProfilerRegionRegion that records timing and line-by-line profiling via line_profiler.
NVTXProfileRegionRegion that records CPU time and emits an NVTX range.
PerfEventProfileRegionTiming region which also sums Linux perf_event_open counters.
ThreadedProfileRegionRegion that records which thread - and which task - each call ran on.
TimeOnlyProfileRegionRegion that records timing, collected once at the end of the run.

Functions

NameDescription
call_site_sourceSource text of the with block starting at lineno in filename.
function_sourceSource file, starting line and text of a decorated function.

AggregateProfileRegionclass#

class AggregateProfileRegion(region_name, config, tags=())

Low-memory region that records aggregate timing statistics only.

configattributeinstance attribute#

config = config

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source

num_callsproperty#

num_calls

ptrproperty#

ptr

add_functionmethod#

def add_function(func)

aggregate_snapshotmethod#

def aggregate_snapshot()

get_aggregatemethod#

def get_aggregate()

mark_writtenmethod#

def mark_written()

set_sourcemethod#

def set_source(filename, lineno, text)

wrapmethod#

def wrap(func)

BaseProfileRegionclass#

class BaseProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Base class providing shared profiling logic.

Handles start/end time buffering and call counting. The buffers grow on demand and are copied out once, at the end of the run, by ProfileManager.finalize() - regions never touch HDF5 themselves.

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

wrapmethod#

def wrap(func)

Wrap a function for profiling.

Subclasses must override this method to implement the appropriate profiling behavior.

CUDATimingNVTXProfileRegionclass#

class CUDATimingNVTXProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: CUDATimingProfileRegion

Region that records CPU/CUDA-event timing and emits an NVTX range.

wrapmethod#

def wrap(func)

Wrap a function with CPU/CUDA timing and an NVTX range.

Inherited from scope_profiler.region_profiler.CUDATimingProfileRegion

gpu_durationsattributeinstance attribute#

gpu_durations = np.empty(self.capacity, dtype=np.int64)

get_gpu_durations_numpymethod#

def get_gpu_durations_numpy() -> np.ndarray

Synchronize recorded CUDA events and return device durations in ns.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

CUDATimingProfileRegionclass#

class CUDATimingProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: TimeOnlyProfileRegion

Region that records CPU timing plus CUDA-event elapsed device time.

gpu_durationsattributeinstance attribute#

gpu_durations = np.empty(self.capacity, dtype=np.int64)

get_gpu_durations_numpymethod#

def get_gpu_durations_numpy() -> np.ndarray

Synchronize recorded CUDA events and return device durations in ns.

wrapmethod#

def wrap(func)

Wrap a function to measure CPU enqueue time and device elapsed time.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

DisabledProfileRegionclass#

class DisabledProfileRegion(BaseProfileRegion)

Bases: BaseProfileRegion

Profiling region that performs no measurements.

Used when profiling is disabled but code paths must remain valid.

appendmethod#

def append(start, end)

Ignored: no data recorded.

get_durations_numpymethod#

def get_durations_numpy()

Return an empty array since nothing is recorded.

wrapmethod#

def wrap(func)

Return the original function unchanged — no wrapper, no overhead.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

FullProfileRegionclass#

class FullProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: BaseProfileRegion

Region that records both timing and LIKWID metrics, and writes to HDF5.

This is the most complete profiling mode: users obtain LIKWID markers and nanosecond-resolution timing.

likwid_marker_startattributeinstance attribute#

likwid_marker_start = pylikwid.markerstartregion

likwid_marker_stopattributeinstance attribute#

likwid_marker_stop = pylikwid.markerstopregion

wrapmethod#

def wrap(func)

Wrap a function to measure time and collect LIKWID metrics.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

LineProfilerRegionclass#

class LineProfilerRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: BaseProfileRegion

Region that records timing and line-by-line profiling via line_profiler.

Uses line_profiler to collect per-line execution statistics for decorated functions. Also records nanosecond timestamps.

Line-by-line profiling is most useful with the decorator (wrap) path, which automatically registers the function with the line profiler. When used as a context manager, the profiler is enabled/disabled around the block - any functions previously added via the decorator path will be profiled while the context is active.

add_functionmethod#

def add_function(func) -> None

Register a function for line-by-line profiling.

enter_framemethod#

def enter_frame(frame)

Enter this region while registering frame with line_profiler.

enter_timing_onlymethod#

def enter_timing_only()

Enter this region without registering or enabling line_profiler.

get_statsmethod#

def get_stats()

Return the line_profiler stats object.

manual_line_recordsmethod#

def manual_line_records(unit: float = 1e-09) -> list

Return manually traced line timings in the persisted record shape.

print_statsmethod#

def print_stats()

Print line-by-line profiling statistics.

record_line_timingmethod#

def record_line_timing(frame, lineno: int, duration_ns: int) -> None

Record one line timing sample from recursive CLI tracing.

wrapmethod#

def wrap(func)

Wrap a function to measure execution time and collect line-by-line stats.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

NVTXProfileRegionclass#

class NVTXProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: TimeOnlyProfileRegion

Region that records CPU time and emits an NVTX range.

NVTX is an annotation API, not a GPU timing API. Nsight Systems and Nsight Compute consume the ranges and correlate them with GPU work. The normal CPU timestamps are retained in the scope-profiler result.

wrapmethod#

def wrap(func)

Wrap a function with both CPU timing and an NVTX range.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

PerfEventProfileRegionclass#

class PerfEventProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: TimeOnlyProfileRegion

Timing region which also sums Linux perf_event_open counters.

Counters are opened per active invocation. This makes nested and recursive regions correct without sharing a mutable counter state, at the intended cost of an extra kernel round trip per selected event.

perf_event_totalsmethod#

def perf_event_totals() -> PerfEventTotals

Counter totals accumulated by completed calls in this region.

wrapmethod#

def wrap(func)
Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

ThreadedProfileRegionclass#

class ThreadedProfileRegion(region_name: str, config: ProfilingConfig, tags=())

Bases: BaseProfileRegion

Region that records which thread - and which task - each call ran on.

Selected by track_threads=True. Two things change against :class:TimeOnlyProfileRegion:

  • every thread gets its own buffers and its own scope stack, so concurrent calls no longer overwrite each other’s reserved slots or close each other’s scopes;
  • each recorded call carries the lane it ran on, so the nesting reconstruction can group calls into stacks that really are stacks (see :func:~scope_profiler.concurrency.lane_ids).

With track_async=True a call additionally carries the id of the asyncio task or greenlet it ran in, and the time that task spent suspended inside the call - the await time of that one call, as opposed to the task’s total.

The per-call cost over :class:TimeOnlyProfileRegion is one thread-local lookup plus, in async mode, two integer reads off the current task record. Nothing here allocates or locks per call.

num_callsproperty#

num_calls: int

Times this region was entered, over every thread, for the process.

threadsproperty#

threads: list

Thread records that have entered this region, in index order.

closed_slotsmethod#

def closed_slots()

Not meaningful per lane; :meth:snapshot_arrays applies the mask.

None, as the base class means it: every slot qualifies, and there is no mask for a caller to apply. A threaded region keeps no buffers of its own, so there is nothing here to mask in the first place.

mark_writtenmethod#

def mark_written() -> None

Rewind every lane, keeping calls that are still running.

open_slotsmethod#

def open_slots() -> np.ndarray

Slots still running, over every lane. For diagnostics only.

snapshot_arraysmethod#

def snapshot_arrays()

This run’s completed calls, concatenated over every thread.

Returns

tuple or None (start_times, end_times, None, None, None, thread_ids, task_ids, await_ns) in nanoseconds, laid out on the snapshot tuple :meth:ProfileManager._snapshot_regions documents, or None when this region recorded nothing since the last finalize(). task_ids and await_ns are None unless async tracking is on.

wrapmethod#

def wrap(func)

Wrap a function so its calls are recorded on the caller’s thread.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

TimeOnlyProfileRegionclass#

class TimeOnlyProfileRegion(BaseProfileRegion)

Bases: BaseProfileRegion

Region that records timing, collected once at the end of the run.

wrapmethod#

def wrap(func)

Wrap a function to measure its execution time.

Inherited from scope_profiler.region_profiler.BaseProfileRegion

buffer_limitattributeinstance attribute#

buffer_limit = config.buffer_limit

capacityattributeinstance attribute#

capacity = self.buffer_limit

configattributeinstance attribute#

config = config

end_timesattributeinstance attribute#

end_times = np.zeros(self.capacity, dtype=np.int64)

ptrattributeinstance attribute#

ptr = 0

region_nameattributeinstance attribute#

region_name = region_name

source_fileattributeinstance attribute#

source_file = None

source_linenoattributeinstance attribute#

source_lineno = None

source_textattributeinstance attribute#

source_text = None

start_timesattributeinstance attribute#

start_times = np.empty(self.capacity, dtype=np.int64)

tagsattributeinstance attribute#

tags = tuple(tags)

has_sourceproperty#

has_source: bool

Whether this region’s call-site source was captured.

num_callsproperty#

num_calls: int

Times this region was entered, for the lifetime of the process.

Derived rather than counted: the slots in use (ptr) plus whatever earlier finalize() calls already copied out. Keeping it out of __enter__ removes an attribute write from every recorded call.

add_functionmethod#

def add_function(func) -> None

Register a function for profiling. No-op except in LineProfilerRegion.

appendmethod#

def append(start: float, end: float) -> None

Append a start/end time pair to the buffer, growing it if needed.

closed_slotsmethod#

def closed_slots() -> np.ndarray | None

Mask of buffered slots this finalize() should copy out.

A slot qualifies once its end time is written and it has not already gone out with an earlier finalize(). None - the usual case - means every slot qualifies and the caller can skip the mask entirely.

get_durations_numpymethod#

def get_durations_numpy() -> np.ndarray

Return durations (end - start) for buffered entries as a NumPy array.

get_end_times_numpymethod#

def get_end_times_numpy() -> np.ndarray

Return end times offset by the run’s start time.

get_start_times_numpymethod#

def get_start_times_numpy() -> np.ndarray

Return start times offset by the run’s start time.

mark_writtenmethod#

def mark_written() -> None

Record that everything buffered so far has been handed to finalize().

Called by finalize() once the data has been copied out, so that a second run in the same process reports only its own events instead of re-reporting the first run’s. The timestamp buffer rewinds (the arrays are reused; anything past ptr is unread scratch), while num_calls keeps counting for the lifetime of the process — it is the in-memory view of the region, which callers inspect after finalize().

A call still running has a slot reserved that finalize() did not copy out, so it must survive the rewind. It is moved to the front of the buffer rather than pinning everything behind it: leaving the whole buffer in place would make the next finalize() re-report every completed call sitting below it, with a second set of call ids.

The one case that still cannot rewind is a call entered through the decorator form, which keeps its slot index in the wrapper’s own frame where nothing can remap it. Recognisable because such a slot is open without appearing in _scope_ptr_stack. There the buffer stays put and the slots already copied out are recorded instead, so the next finalize() skips them rather than reporting those calls again.

open_slotsmethod#

def open_slots() -> np.ndarray

Buffer slots whose call is still running, in ascending order.

A slot is open until its end time is written, which is what distinguishes a call in flight from one that has returned. Both region forms are covered: the context manager writes its start on entry, the decorator writes nothing until the call returns.

set_sourcemethod#

def set_source(filename, lineno, text) -> None

Record where this region is defined, the first time it is called.

First writer wins: a region name reused at more than one call site keeps only the first location, matching how their timings are already pooled together under one name (see issue #161).

call_site_sourcefunction#

def call_site_source(filename: str, lineno: int) -> str | None

Source text of the with block starting at lineno in filename.

Falls back to just the call-site line when the enclosing with statement cannot be located (e.g. the file cannot be parsed), so a region still gets some location information rather than none.

function_sourcefunction#

def function_source(func) -> tuple[str, int, str] | None

Source file, starting line and text of a decorated function.

Returns None when the source cannot be recovered (e.g. a function defined interactively or via exec).