Skip to content
Projects

Quickstart

Install for this page: pip install "scope-profiler[pproc]". The base install is sufficient through the text summary; pproc adds the plotted example in step 4.

This page walks through the lowest-touch workflow first: mark regions, run, and inspect. It then shows the equivalent Python lifecycle API.

Add regions where the application has meaningful phases; no in-source session or setup call is required:

import scope_profiler as sp
def main():
with sp.region("iteration", tags=["solver"]):
evolve()
main()

Run the script through the CLI:

Terminal window
scope-profiler run example.py -o profile.h5

The same script run with python example.py leaves the regions disabled and writes no profile. To trace functions that were not explicitly marked, opt in to recursive tracing:

Terminal window
scope-profiler run --recursive example.py

You can select tagged regions and filter recursive function names without editing the script again:

Terminal window
scope-profiler run --tag solver --include 'my_app.*' example.py

For a named application entrypoint, use --entrypoint main.

Use ProfileManager.session() as the default entry point. Configuration arguments are passed directly to the session:

from scope_profiler import ProfileManager
with ProfileManager.session():
# Profile regions inside this block.
run_application()

All regions created inside the session — even in other modules — share its configuration.

For a callable entrypoint, sp.run() provides the same setup/finalize safety without a context-manager block:

import scope_profiler as sp
sp.run(run_application, output="profile.h5")

Use sp.setup(auto_finalize=True) when a long-running script needs one startup call and cannot wrap its whole application in a with block.

Use @ProfileManager.profile to wrap an entire function:

@ProfileManager.profile("matrix_multiply")
def matrix_multiply(a, b):
return a @ b

The decorator also works without an explicit name — it uses the function name by default:

@ProfileManager.profile
def matrix_multiply(a, b):
return a @ b

To include nested Python calls made by a decorated function:

with ProfileManager.session(recursive_profile=True):
@ProfileManager.profile("solver_step")
def solver_step():
return advance_state() # nested calls are recorded automatically

Use ProfileManager.region() for finer-grained control:

for step in range(num_steps):
with ProfileManager.region("time_step"):
evolve(state, dt)
with ProfileManager.region("io"):
write_checkpoint(state)

The two styles can be mixed freely.

When the with block exits, the session writes all buffered data, merges per-rank HDF5 files, and prints a summary. Use return_results=True to keep the finalized results in memory:

with ProfileManager.session(return_results=True) as run:
run_application()
results = run.results

Output:

╭─────────────────────────────────────────────────────────╮
│ region % session total [s] │
├─────────────────────────────────────────────────────────┤
│ scope_profiler.session 100.00% 0.001488 │
│ └─ (own) 41.40% 0.000616 │
│ └─ matrix_multiply (100x) 5.59% 0.000083 │
│ └─ time_step (1kx) 53.01% 0.000789 │
╰─────────────────────────────────────────────────────────╯
╭─ Info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Summary: profiling_data.h5 (1 rank(s)) │
│ │
│ Explore: │
│ Inspect: scope-profiler inspect ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ TUI: scope-profiler tui ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ │
│ Visualize and export: │
│ Plot: scope-profiler plot default ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o plots --show │
│ Report: scope-profiler report ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o report.html │
│ Export: scope-profiler export plot-data ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o data │
│ Lines: scope-profiler line-profile ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ │
│ Compare runs: │
│ Diff: scope-profiler diff BASE.h5 CANDIDATE.h5 │
│ Check: scope-profiler check BASE.h5 CANDIDATE.h5 │
│ │
│ Durations are in seconds. │
│ Regions may nest, so the summed total can exceed the wall-clock time. │
│ % session uses wall-clock coverage; overlapping recursive calls count once. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

The same table is available from ProfilingResults.print_summary() and from scope-profiler inspect; pass verbose=False to finalize() to suppress it.

After finalization the timing data is saved to profiling_data.h5 (default). Use the built-in CLI to generate a Gantt chart:

Terminal window
scope-profiler plot default profiling_data.h5 --show

Example Gantt timeline with nested solver phases

See Plotting with the CLI for the other charts and exports it can produce. Or load the data programmatically:

from scope_profiler import load
results = load("profiling_data.h5")
# The quickest look: a summary table of every region.
results.print_summary()
# Or region by region (durations are in seconds).
for region in results:
print(f"{region.name}: {region.num_calls} calls, "
f"avg {region.average_duration:.4f} s")

load() reads whichever format the file is in — HDF5 or JSON — so a path is all you need. read_h5() and read_json() remain available when the format is already known.

from scope_profiler import ProfileManager
with ProfileManager.session():
@ProfileManager.profile("main")
def main():
x = 0
for i in range(10):
with ProfileManager.region("iteration"):
x += 1
main()
Terminal window
python example.py
╭────────────────────────────────────────────────────────╮
│ region % session total [s] │
├────────────────────────────────────────────────────────┤
│ scope_profiler.session 100.00% 0.000088 │
│ └─ (own) 60.54% 0.000053 │
│ └─ main 39.46% 0.000035 │
│ │ └─ (own) 30.46% 0.000027 │
│ │ └─ iteration (10x) 9.00% 0.000008 │
╰────────────────────────────────────────────────────────╯
╭─ Info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Summary: profiling_data.h5 (1 rank(s)) │
│ │
│ Explore: │
│ Inspect: scope-profiler inspect ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ TUI: scope-profiler tui ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ │
│ Visualize and export: │
│ Plot: scope-profiler plot default ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o plots --show │
│ Report: scope-profiler report ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o report.html │
│ Export: scope-profiler export plot-data ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 -o data │
│ Lines: scope-profiler line-profile ../../../../../tmp/scope-profiler-docs-output-lat7ezsv/profiling_data.h5 │
│ │
│ Compare runs: │
│ Diff: scope-profiler diff BASE.h5 CANDIDATE.h5 │
│ Check: scope-profiler check BASE.h5 CANDIDATE.h5 │
│ │
│ Durations are in seconds. │
│ Regions may nest, so the summed total can exceed the wall-clock time. │
│ % session uses wall-clock coverage; overlapping recursive calls count once. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯