Skip to main content

scope-profiler

This module provides a unified profiling system for Python applications, with optional integration of LIKWID markers using the pylikwid marker API for hardware performance counters.

It allows you to:

  • Configure profiling globally via a singleton ProfilingConfig.
  • Collect timing data via context-managed profiling regions.
  • Use a clean decorator syntax to profile functions.
  • Optionally record time traces in HDF5 files.
  • Automatically initialize and close LIKWID markers only when needed.
  • Print aggregated summaries of all profiling regions.

Install

Install from PyPI:

pip install scope-profiler

Usage

To set up the configuration, create an instance of ProfilingConfig and add it to the ProfileManager, this should be done once at application startup and will persist until the program exits or is explicitly finalized (see below). Note that the config applies to any profiling contexts created (even in other files) after it has been initialized.

from scope_profiler import ProfileManager

# Setup global profiling configuration
ProfileManager.setup(
    use_likwid=False,
    recursive_profile=False,
    time_trace=True,
    flush_to_disk=True,
)

# Profile the main() function with a decorator
@ProfileManager.profile("main")
def main():
    x = 0
    for i in range(10):
        # Profile each iteration with a context manager
        with ProfileManager.profile_region(region_name="iteration"):
            x += 1

# Call main
main()

# Finalize profiler
ProfileManager.finalize()

Execution:

 python test.py
Region: main
  Total Calls : 1
  Total Time  : 0.001503709 s
  Avg Time    : 0.001503709 s
  Min Time    : 0.001503709 s
  Max Time    : 0.001503709 s
  Std Dev     : 0.0 s
----------------------------------------
Region: iteration
  Total Calls : 10
  Total Time  : 3.832e-06 s
  Avg Time    : 3.832e-07 s
  Min Time    : 2.08e-07 s
  Max Time    : 8.75e-07 s
  Std Dev     : 2.2431888016838885e-07 s
----------------------------------------

Example plots

scope-profiler pproc turns an HDF5 profiling file into Gantt, flame, duration, and speedup charts (see Flame graphs below for details). The plots here come from examples/generate_readme_figures.py, a small mock timestep loop with nested and self-recursive regions, and are saved to figures/:

python examples/generate_readme_figures.py

Gantt chart of a mock timestep loop

Average duration per region

The flame graph for the same run is shown in Flame graphs below.

Overhead

The profiling overhead per call depends on the region type. The benchmark below (examples/benchmark_overhead.py) measures each mode against a bare function call:

Profiling overhead by region type

The two modes most relevant to HPC — NCallsOnly and TimeOnly — add roughly 0.09 µs and 0.75 µs per instrumented call respectively.

Profiling can also be fully deactivated at setup time (profiling_activated=False) to reduce the overhead to ~0.03 µs — barely above a bare function call — making it safe to leave instrumentation in production code and toggle it on only when needed.

The LineProfiler mode is intentionally heavier (~41 µs/call) because line_profiler traces every source line. It is designed for targeted debugging of individual functions, not for always-on use in hot loops.

Recursive profiling of nested calls

You can profile nested Python calls from one decorated entrypoint:

from scope_profiler import ProfileManager

ProfileManager.setup(recursive_profile=True)


def leaf(x):
    return x + 1


def inner(x):
    return leaf(x) * 2


@ProfileManager.profile("entry")
def entry():
    return sum(inner(i) for i in range(3))


entry()
ProfileManager.finalize()

When enabled, the profiler records regions for nested calls using fully qualified names (for example, my_module.inner), in addition to the main decorated region.

Zero-instrumentation CLI profiling

You can profile a whole script without touching its source, similar to python -m cProfile:

scope-profiler run my_script.py [script args...]
# equivalently: python -m scope_profiler run my_script.py [script args...]

Every Python function call the script makes is recorded as its own region under a name derived from its module and qualified name, using the same recursive tracer as recursive_profile=True above. By default only the script's own code is instrumented (the standard library and installed packages are skipped) to keep overhead low; pass --all to trace everything. Results are written to profiling_data.h5 by default (-o/--outfile to change it), and a per-region summary is printed unless -q/--quiet is given.

See examples/ex_cli_profiling.py for a script with no scope-profiler imports at all, run with:

scope-profiler run examples/ex_cli_profiling.py

Profiling self-recursive functions

A single region can also be safely re-entered by a recursive function - each call gets its own slot in the region's buffer, so nested calls don't overwrite each other's timing data. This works with both the decorator and context-manager forms:

from scope_profiler import ProfileManager

ProfileManager.setup()


@ProfileManager.profile("fibonacci")
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


def fibonacci_context_manager(n):
    with ProfileManager.profile_region("fibonacci_ctx"):
        if n < 2:
            return n
        return fibonacci_context_manager(n - 1) + fibonacci_context_manager(n - 2)


fibonacci(10)
fibonacci_context_manager(10)
ProfileManager.finalize()

Both fibonacci and fibonacci_ctx will report one call per recursive invocation, each with correct, non-overlapping timing data.

Flame graphs

Because each call - including recursive re-entries of the same region - now has its own correctly nested (start, end) interval, the call stack can be reconstructed straight from the timing data and rendered as a flame graph, with recursion showing up as a narrowing tower of frames - as with refine_mesh below, from the same run shown in Example plots:

Flame graph of a mock timestep loop

scope-profiler pproc generates flame_plot.png alongside the Gantt chart for every run:

scope-profiler pproc profiling_data.h5 --show -o figures

Or programmatically:

from scope_profiler.h5reader import ProfilingH5Reader
from scope_profiler.plotting_scripts import plot_flame

reader = ProfilingH5Reader("profiling_data.h5")
plot_flame(reader, filepath="flame_plot.png")

Gantt and flame charts (and plot_speedup) always color the same region the same way. Pass --cmap (or cmap= on the plot_* functions) to use a different matplotlib colormap than the default tab20:

scope-profiler pproc profiling_data.h5 --cmap viridis -o figures

By default the flame graph covers rank 0, since it represents a single execution's call stack; pass ranks=[...] to render one flame graph per requested rank.

Exporting plot data

Every plot_* function accepts a data_filepath argument that writes the exact data behind the chart to a file, so it can be re-parsed and re-plotted later without the original HDF5 file. data_format selects "csv" (default) or "json":

plot_gantt(reader, filepath="gantt_plot.png", data_filepath="gantt_data.csv")
plot_gantt(
    reader,
    filepath="gantt_plot.png",
    data_filepath="gantt_data.json",
    data_format="json",
)

The JSON payload additionally includes a colors map (region or file label to #rrggbb) matching the colors used in the matplotlib plot, so a JavaScript charting library like Plotly can reproduce the same look.

scope-profiler pproc --export-data does the same for every plot in one run, writing gantt_data, flame_data, durations_data, and (for multiple input files) speedup_data alongside the PNGs. Pass --export-data-format json to get .json files instead of the default .csv:

scope-profiler pproc profiling_data.h5 -o figures --export-data
scope-profiler pproc profiling_data.h5 -o figures --export-data --export-data-format json

Pass --skip-plot-images (requires --export-data) to skip rendering the PNGs entirely and only write the exported data plus region_statistics.json — useful when a website renders charts client-side (e.g. with Plotly) straight from the JSON:

scope-profiler pproc profiling_data.h5 -o figures \
  --export-data --export-data-format json --skip-plot-images

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scope_profiler-0.2.1.tar.gz (42.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scope_profiler-0.2.1-py3-none-any.whl (45.9 kB view details)

Uploaded Python 3

File details

Details for the file scope_profiler-0.2.1.tar.gz.

File metadata

  • Download URL: scope_profiler-0.2.1.tar.gz
  • Upload date:
  • Size: 42.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for scope_profiler-0.2.1.tar.gz
Algorithm Hash digest
SHA256 faf5b5e9a625f6d8060e46d96fd3abd33e973faa2d3d43f67b0513f82dc1834b
MD5 6b023786807b30c0f59eb6fb4a1ceadd
BLAKE2b-256 88f617ad3f600eade3ee6d41622e4ff4903d824ce8b7d04c3dec3b7483f5cbf7

See more details on using hashes here.

Provenance

The following attestation bundles were made for scope_profiler-0.2.1.tar.gz:

Publisher: publish.yml on max-models/scope-profiler

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scope_profiler-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: scope_profiler-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 45.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for scope_profiler-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 594e2f85140df08659cd36989040516e317a8400ac56e58ce3df4c481f3a2d59
MD5 894bb9b6a69810c02ab00117d36d163b
BLAKE2b-256 675bce8b2b571de4de15a571bad070c29c4970ec663077c64608947f00ca74fd

See more details on using hashes here.

Provenance

The following attestation bundles were made for scope_profiler-0.2.1-py3-none-any.whl:

Publisher: publish.yml on max-models/scope-profiler

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.5.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

This release

0.2.1 This release

2 files

0.2

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page