Profiling modes#

The quickstart covers the shortest path from a few regions to a profile. This page collects the options for choosing how much code to measure and how much data to retain. Start with explicit regions; enable broader modes only when the extra detail answers a specific question.

Explicit regions#

The default CLI mode activates regions already present in the application:

scope-profiler run app.py -o profile.h5

Use tags to select a subset of regions. A repeated --tag uses OR semantics:

import scope_profiler as sp

with sp.region("linear_solve", tags=["solver", "expensive"]):
    solve()
scope-profiler run --tag solver --tag io app.py

Recursive tracing#

For code without explicit regions, or when function-level attribution is needed, opt in to recursive tracing:

scope-profiler run --recursive app.py

--include and --exclude accept shell-style globs for recursively traced function names. --all additionally includes installed packages and the standard library:

scope-profiler run --recursive \
  --include 'my_app.solver.*' \
  --exclude '*logging*' \
  app.py

Use --entrypoint main to name the traced application entrypoint. Recursive tracing has more overhead than explicit regions, so filters are recommended for large applications.

Python lifecycle shortcuts#

The CLI is equivalent to a managed Python session. For a callable entrypoint, use sp.run():

import scope_profiler as sp

sp.run(main, output="profile.h5")

Use the session decorator when the application should own its entrypoint:

@sp.session(file_path="profile.h5")
def main():
    run_application()

For a script that cannot use a context manager, opt in to a guarded exit hook:

sp.setup(file_path="profile.h5", auto_finalize=True)

Output and summaries#

The output extension selects the format. --no-output is useful for checking regions or printing a summary without leaving a file behind:

scope-profiler run -o profile.h5 app.py
scope-profiler run -o profile.json app.py
scope-profiler run -o report.html app.py
scope-profiler run --no-output app.py

Use --label to identify a run in comparisons:

scope-profiler run --label baseline app.py

Concurrency#

Thread and asynchronous task tracking are opt-in Python API modes:

with sp.session(track_threads=True):
    run_parallel_work()

with sp.session(track_async=True):
    asyncio.run(main())

track_async=True also tracks task suspension time, so a region held across an await can be separated into running and awaiting time.

Reducing trace volume#

Aggregation mode stores counts and timing totals without per-call events:

scope-profiler run --aggregation-mode app.py

For periodic workloads, pause profiling between phases or use sample_every():

import scope_profiler as sp
from scope_profiler import ProfileManager

with sp.session(file_path="profile.h5"):
    with ProfileManager.sample_every(10) as profile_step:
        for step in range(steps):
            with profile_step(step):
                simulation.step()

See Configuration for line profiling, GPU timing, NVTX, MPI, LIKWID, memory profiling, and HDF5 tuning options.

Notebook workflows#

Load the IPython magics when profiling from a notebook:

%load_ext scope_profiler.ipython_magics

Use %%scope for a measured cell, %%scope_recursive when nothing is instrumented, %%scope_line for line timings, and %%scope_agg for very hot regions. %scope_compare, %scope_load, and %scope_export support the follow-up analysis workflow. See Jupyter/IPython magics for the complete reference.