CLI reference#
Install for this page:
pip install "scope-profiler[pproc]"enables the profiling, inspection, report, export, plot, and plotted-TUI examples. A text-only TUI can instead usescope-profiler[tui]; line profiling needsscope-profiler[line-profiler].
All subcommands live under the single scope-profiler executable (also
runnable as python -m scope_profiler).
pytest integration#
The package also exposes an opt-in pytest plugin. It writes a regular
HDF5 profile with one region per selected test, named
pytest::<nodeid>::<phase>:
pytest --scope-profile --scope-profile-out pytest-profile.h5
scope-profiler inspect pytest-profile.h5 --regions-only --sort total
scope-profiler plot durations pytest-profile.h5 -o pytest-plots
Only the test call phase is measured by default. To include fixture
setup and teardown, pass --scope-profile-phases=all. A profiling TOML
file can be shared with other runs using
--scope-profile-config profiling.toml.
The plugin deliberately uses an independent manager, so tests that
configure the process-wide ProfileManager do not interfere with it. It
times complete test phases rather than recursively tracing pytest
internals. When using pytest-xdist, omit --scope-profile: parallel
workers cannot safely write one HDF5 profile yet.
scope-profiler tui#
Open an interactive browser for a profiling HDF5 file. The Plots
section reuses the plotting functions from scope-profiler plot:
scope-profiler tui profiling_data.h5
Simple duration plots are rendered with Plotext inside the TUI when
selected and update automatically as settings change. Press t to
refresh them manually if needed. Press g to display a plot in a
Matplotlib window, or p to open it as an interactive Plotly figure in
a browser. Press s to save the Matplotlib version as PNG. Saved plots
go to <file-stem>_plots/ by default; use --plot-output DIR to choose
another directory.
For the Flame plot, press v to export the selected data to .prof
format and open it in Snakeviz (pip install snakeviz).
When a plot is selected, its settings panel shows the regions matched by the include/exclude filters as you type. It also supports rank ranges, colormap, log scale, duration metrics, duration sorting and top-N selection, histogram bin count, and imbalance metric. The include and exclude fields autocomplete region names from the loaded profile and accept comma-separated values. Press Apply settings before displaying or saving the plot.
Available TUI plot types include Gantt, flame chart, flame graph,
durations, timeseries, histogram, imbalance, and any LIKWID metrics
recorded in the file. Only the durations plot is rendered with Plotext
inside the TUI; the other plots can be shown with Matplotlib, opened
with Plotly, or saved as files. Install the optional TUI/plotting
dependencies with pip install "scope-profiler[pproc]".
scope-profiler run#
Run a script with its explicit @profile and region() instrumentation
enabled. Recursive function tracing is opt-in with --recursive; add
--all to include standard-library and installed-package calls as well.
usage: scope-profiler run [-h] [-o OUTFILE | --no-output] [-q] [--recursive | --no-recursive] [--include GLOB] [--exclude GLOB] [--all] [--label LABEL] [--tag TAG] [--entrypoint NAME] [--line-profile] [--memory-profile]
[--buffer-limit BUFFER_LIMIT]
script ...
Flag |
Description |
|---|---|
|
Path of the output file (default: |
|
Keep results in memory and do not write a profile file |
|
Suppress the per-region summary printed after the run |
|
Trace every Python function in the script; explicit regions only by default |
|
Disable recursive tracing, overriding the configuration file |
|
Recursively trace all calls, including standard-library and installed-package calls |
|
Store a short label with the run for comparison and reporting |
|
Record only explicit regions carrying the given tag; repeatable |
|
Name the recursively traced entrypoint region |
|
Include only recursively traced function names matching this glob; repeatable |
|
Exclude recursively traced function names matching this glob; repeatable |
|
Also collect line-by-line timings via |
|
Record allocations with Memray in an adjacent |
|
Initial buffer capacity per region; grows as needed (default: 1024) |
scope-profiler run my_script.py [script args...]
The runner performs setup before loading the script and finalizes it afterward. This means explicitly instrumented scripts do not need an in-source session:
import scope_profiler as sp
@sp.profile("main")
def main():
with sp.region("iteration"):
work()
main()
python my_script.py leaves these instrumentation points disabled and
writes nothing; scope-profiler run my_script.py activates and records
them. Use scope-profiler run --recursive my_script.py for an
uninstrumented script. The explicit session(), setup(), and
finalize() APIs remain unchanged.
Output formats#
The extension of -o chooses what is written, and HDF5 remains the
default:
scope-profiler run -o profile.h5 my_script.py # HDF5 (the default)
scope-profiler run -o profile.json my_script.py # a JSON profile
scope-profiler run -o profile.json.gz my_script.py # the same, gzip-compressed
scope-profiler run -o report.html my_script.py # a rendered HTML report
Use --no-output when only an in-memory summary is needed; it
suppresses the default profiling_data.h5 file. --label stores a
short run name in the profile, which is useful when comparing several
runs. --no-recursive can override a configuration file that enables
recursive tracing.
Repeat --tag to select explicitly defined regions by tag; a region is
kept when it has at least one of the requested tags. --include and
--exclude apply to recursively traced function names and accept
shell-style globs.
A JSON profile holds everything the HDF5 file does — every call’s
timestamps, the lane tables, LIKWID counters, line-profiler records — in
integer nanoseconds, so nothing is lost by choosing it. inspect,
plot, report, diff, tui and the exporters all read one wherever
they read an HDF5 file, and scope_profiler.read_json() (or the
format-dispatching read_profile()) loads it in Python. It is the
format to reach for when the consumer is not this package: a browser,
jq, a code review, or a machine without h5py. HDF5 stays the better
choice for a long run: it is read back column by column rather than
whole (a summary read never touches the event columns at all), and it is
the only format the parallel and rank-by-rank writers can produce. On
size the two are closer than they look — for a run of a few hundred
thousand events, plain JSON is roughly a third larger than the HDF5 file
and --gzip is a few times smaller than it, but a gzipped document has
to be decompressed and parsed in full before the first number comes out.
The run itself always writes HDF5 and the requested format is rendered
from it afterwards, so -o never changes what a run measures or costs.
Under MPI the conversion happens on rank 0, once the merged file exists.
scope-profiler inspect#
Print what is inside a profiling file — the run metadata in full, and an overall statistics line per region — without producing any plots. Useful for checking which environment a run came from, and where its time went, at a glance.
usage: scope-profiler inspect [-h] [--include INCLUDE [INCLUDE ...]]
[--exclude EXCLUDE [EXCLUDE ...]]
[--ranks RANKS [RANKS ...]]
[--sort {total,calls,avg,min,max,first,last,std,p50,p95,p99,imbalance,name}] [--full]
[--columns {region,ranks,calls,total,avg,min,max,first,last,std,p50,p95,p99,imbalance} [...]]
[--source NAME [NAME ...]]
[--metadata-only | --regions-only]
files [files ...]
Flag |
Description |
|---|---|
|
Only report regions matching these regex patterns |
|
Skip regions matching these regex patterns |
|
Restrict region statistics to these ranks, e.g. |
|
Order regions by any region-table statistic, or by |
|
Restrict the region table to selected columns; default is |
|
Print long metadata values ( |
|
Print the captured call-site source (the |
|
Also write the metadata of every inspected file to this JSON file |
|
Suppress the printed summary (useful with |
|
Print only the metadata section |
|
Print only the region statistics |
scope-profiler inspect profiling_data.h5 --regions-only
scope-profiler inspect 'run_*.h5' --regions-only --sort calls
scope-profiler inspect profiling_data.h5 --regions-only --columns region ranks calls total avg
scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
scope-profiler inspect profiling_data.h5 --source solve assemble
Example output:
==============================================================================
profiling_data.h5
1 rank(s), 3 region(s), 0.05 MiB, 0.00287576 s wall clock, 0.0041239 s total (setup to finalize)
==============================================================================
╭─────────────────────────────────────────────────────────╮
│ region % session total [s] │
├─────────────────────────────────────────────────────────┤
│ scope_profiler.session 100.00% 0.002876 │
│ └─ (own) 45.51% 0.001309 │
│ └─ matrix_multiply (100x) 5.03% 0.000145 │
│ └─ time_step (1kx) 49.46% 0.001422 │
╰─────────────────────────────────────────────────────────╯
╭─ Info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Summary: Regions (3) │
│ │
│ Explore: │
│ Inspect: scope-profiler inspect ../../../../..profiling_data.h5 │
│ TUI: scope-profiler tui ../../../../..profiling_data.h5 │
│ │
│ Visualize and export: │
│ Plot: scope-profiler plot default ../../../../..profiling_data.h5 -o plots --show │
│ Report: scope-profiler report ../../../../..profiling_data.h5 -o report.html │
│ Export: scope-profiler export plot-data ../../../../..profiling_data.h5 -o data │
│ Lines: scope-profiler line-profile ../../../../..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. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Region durations are in seconds, aggregated over the selected ranks. See HDF5 output & post-processing from Python for what each metadata field means.
Showing a region’s source#
A region can remember the with block or decorated function it was
defined with - captured once when the region is first created, but only
if the run was profiled with capture_region_source=True (off by
default; see Configuration for its cost and
HDF5 output & post-processing from Python for the Python API). --source prints
it, after the region table:
scope-profiler inspect profiling_data.h5 --source solve assemble
Source (2)
solve (kernels.py:42)
with ProfileManager.region("solve"):
return solver.step(state)
assemble (kernels.py:18)
@ProfileManager.profile("assemble")
def assemble(size):
return build_matrix(size)
A name with nothing captured (capture_region_source was left at its
default, the file predates this feature, or the region was only ever
created by the recursive tracer) prints “source not captured” instead of
failing the whole command; an unknown name prints the list of regions
the file actually has.
Exporting metadata to JSON#
--export-metadata writes the metadata of every inspected file to one
JSON document. Values are never clipped there, regardless of --full:
scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
{
"files": [
{
"file_path": "/scratch/run/profiling_data.h5",
"num_ranks": 2,
"metadata": {
"chip_information": "AMD EPYC 9654 96-Core Processor",
"modules": ["profile/base", "gcc/12.3.0", "python/3.11.7"],
"SLURM_JOB_ID": "9988776",
"...": "..."
}
}
]
}
Several files (or a glob) produce one entry each, which makes the export convenient for comparing the environments of a set of runs. The same is available from Python:
from scope_profiler.inspection import collect_file_metadata, write_metadata_json
payload = write_metadata_json("profiling_data.h5", "metadata.json")
payload = collect_file_metadata(["run_1.h5", "run_2.h5"]) # no file written
scope-profiler report#
Write a self-contained HTML report containing the selected per-region statistics and the complete metadata for one or more profiling files. It has no plotting dependency, so it is suitable for copying from a compute node and opening later in a browser.
scope-profiler report profiling_data.h5 # writes report.html
scope-profiler report 'run_*.h5' -o comparison.html --sort calls
scope-profiler report profiling_data.h5 -o report.html --include solve --ranks 0-3
-o/--output names the HTML file (default: report.html).
--include, --exclude, --ranks, --sort, and --columns use the
same meaning as scope-profiler inspect and control the
region-statistics table.
One file gets a full report. It opens with the run’s file, recording time, host and scale, a row of headline numbers - wall time, ranks, regions, the share of the session inside regions, the top hotspot and, for MPI runs, the worst load imbalance - and a few findings worth acting on. Hotspots then ranks the leaves of the call tree by time: regions without nested regions, and the own time a parent spends outside its children, each with the call path it sits on. These add up to the session, so they name the code that costs the time rather than whatever encloses it.
The region-statistics table has the same layout as the terminal summary:
regions indented along the call tree, call counts after the name, and an
(own) row with each parent’s time excluding its children. Clicking a
column header re-sorts the table and drops the tree indentation.
Collapse or expand a region’s nested regions with the arrow beside it.
Clicking a row expands it in place to show the captured call-site
source, tags, and a per-rank calls/total/avg/min/max breakdown.
With more than one rank selected, Load balance lists each region’s
own time per rank - mean, min, max, the slowest rank, its imbalance over
the mean, and the excess time the slowest rank spends above the
average, which orders the table - and Rank comparison sets every
rank side by side, coloring each cell by how far that rank is from the
region’s mean. Runs recorded with line_profiler show each region’s
line profile in its table row: clicking the row shows its per-rank table
and then the profiled function’s source as written and
syntax-highlighted, summed over the selected ranks, with the time on
each line and the hottest one highlighted. A long function scrolls
inside a box of limited height, which opens at the hottest line.
Functions whose region is not in the table - left out by --include,
say - are listed in a separate Line profile section instead.
Several files get a comparison report instead, with the first file
as the baseline. It lists the runs with their wall times, shows what
changed in each run against the baseline - the largest own-time
improvements and regressions, ignoring changes too small to be more than
noise - and puts every region’s duration in every run side by side,
matched by call path, with a switch between total, own, per-call time
and calls and an ordering by largest change. A full report for each run
is written next to the comparison, as <output>-<index>-<label>.html,
and linked from its run table; pass --no-individual-reports to skip
them.
Metadata is always retained in the report. With scope-profiler[pproc]
installed, reports also embed interactive Plotly charts; pass
--no-charts for the compact table-only version. A single run opens on
a region durations chart, whose bars split each region’s total into its
own time and the regions it calls directly (plain bars for an aggregated
profile, which has no timeline), followed by a timeline, both unfolded,
plus a rank-imbalance chart and rank heatmap when more than one rank is
selected. A bar at the top of the page stays there while you scroll. It
holds the region filter and two sliders that limit every chart and table
to the same regions: Regions keeps the N with the most time (it
opens on 500 when there are more; in a comparison, a region’s larger
total over the runs counts), and Depth hides regions nested more
than the chosen number of levels below a top-level call, along with the
timeline’s deeper calls. Charts in collapsed panels are drawn when they
are opened. For flame graphs and flame charts, use
scope-profiler plot flame_graph and scope-profiler plot flame_chart.
A comparison keeps the charts that compare runs: grouped region
durations and a percent-change chart for exactly two runs. When the runs
differ in MPI ranks or OpenMP threads (the omp_num_threads metadata),
scaling charts come first, for each of the eight regions with the most
time on the smallest run, over MPI ranks when they change (even if
threads change too), otherwise over threads. Speedup (smallest run’s
mean call duration over each run’s, against an ideal proportional to the
scale) is for a strong-scaling study, where every run solves the same
total problem. Weak scaling (the same ratio, read as an efficiency
against an ideal of 1) is for a weak-scaling study, where the problem
grows with the run so each rank or core has the same share. Nothing in a
profile records the problem size, so the report cannot tell the two
apart: --scaling strong or --scaling weak keeps the matching chart,
and the default, --scaling both, shows both with the speedup open and
the weak-scaling chart collapsed. Across run sizes the durations table
compares time per rank and the bars show the mean call, since times
summed over ranks grow with the rank count even as a run gets faster.
# Every run has the same work per rank
scope-profiler report 'ranks_*.h5' -o weak.html --scaling weak
Both scaling charts share an x-axis.
--speedup-x {auto,ranks,nodes,threads,cores} chooses it: MPI ranks,
nodes (the number of distinct hosts the ranks ran on), OpenMP threads,
or cores (ranks × threads). auto, the default, is the choice described
above. A named axis has to be recorded by every run - files from older
scope-profiler versions have no node count for a multi-rank run - or the
command stops with an error naming the runs that lack it; if every run
has the same value, the report says so in place of the charts. Buttons
above each scaling chart switch it to any other axis the runs differ in,
without regenerating the report:
scope-profiler report nodes_*.h5 -o scaling.html --speedup-x nodes
Each chart has an Open in new tab button that shows it, as currently filtered and highlighted, on a page of its own. If a profile contains LIKWID or built-in Linux perf events, the report adds hardware-counter tables automatically and plots the first available LIKWID metric. A contents bar, region search, summary links, and expand/collapse controls keep larger reports navigable.
Clicking a region in a table or any chart highlights that region
throughout the report. A chart click leaves the page where it is and
offers a Show in table jump instead; a hotspot click goes straight
to the region’s row. Use Clear highlight beside the search box to
reset it. The charts are built by the same versioned
@scope-profiler/plotly code used for export plot-data on the web.
Both that builder and Plotly itself are embedded in the report, so
opening the file never requires a network connection.
That runtime is most of the file: a report is around 4.8 MB almost
regardless of the profile, of which ~4.7 MB is Plotly. Pass
--charts-cdn to load Plotly from https://cdn.plot.ly instead, which
brings the same report down to about 80 kB - small enough to email or
commit - at the cost of needing a network connection to draw the charts.
The version is pinned to the one this scope-profiler would have
embedded, so both modes render identically, and a report that cannot
reach the CDN says so in place of its charts rather than failing
silently. The tables, statistics and metadata never depend on it.
scope-profiler report profiling_data.h5 -o report.html --charts-cdn
A search box at the top of the report filters which regions are shown,
in both the statistics tables and the charts at once. It takes a
comma-separated list of terms, each matched case-insensitively against
any part of a region name – solve, exchange keeps both - and a term
prefixed with ^ is anchored to the start, so ^field keeps
field_energy but not apply_field_boundary. An empty box shows
everything. This is the same syntax the profiling-data site uses, and
the charts are refiltered by the builder that drew them rather than by a
second implementation.
scope-profiler diff#
Compare region statistics between two merged HDF5 profiling files, region by region, so a regression (or an improvement) between two runs - two commits, two configs, two job sizes - shows up as a single table.
usage: scope-profiler diff [-h] [--include INCLUDE [INCLUDE ...]]
[--exclude EXCLUDE [EXCLUDE ...]]
[--ranks RANKS [RANKS ...]]
[--metric {total,avg,min,max,p50,p95,p99,imbalance,calls}]
[--sort {delta,pct,name}] [--threshold PCT]
file_a file_b
Flag |
Description |
|---|---|
|
Only compare regions matching these regex patterns |
|
Skip regions matching these regex patterns |
|
Restrict the statistics to these ranks, e.g. |
|
Statistic to compare: |
|
Order regions by descending ` |
|
Only show regions whose absolute percent change is at least this many percent |
scope-profiler diff baseline.h5 candidate.h5
scope-profiler diff baseline.h5 candidate.h5 --metric avg --threshold 5
scope-profiler check#
Use check in CI to fail when a candidate exceeds a performance budget:
scope-profiler check baseline.h5 candidate.h5 --max-regression 5
The command prints the comparison table and returns exit code 1 when
any region is more than 5% slower. Use --fail-on-new to treat newly
appearing regions as failures, and --metric p95 or
--metric imbalance to enforce a tail-latency or MPI-balance budget.
Example output:
Baseline: ../../../../..baseline.h5 (1 rank)
Candidate: ../../../../..candidate.h5 (1 rank)
Regression budget: +5% (total)
Regression check: FAIL
╭────────────────────────┬─────────────────┬─────────────────┬─────────────┬─────────────╮
│ region │ total [s] (a) │ total [s] (b) │ delta │ delta [%] │
├────────────────────────┼─────────────────┼─────────────────┼─────────────┼─────────────┤
│ solve │ 0.00306822 │ 0.00606759 │ +0.00299937 │ +97.76% │
│ scope_profiler.session │ 0.00432668 │ 0.00846089 │ +0.0041342 │ +95.55% │
│ setup │ 0.00108717 │ 0.00107669 │ -1.0479e-05 │ -0.9639% │
│ teardown │ - │ 0.00107405 │ +0.00107405 │ - │
╰────────────────────────┴─────────────────┴─────────────────┴─────────────┴─────────────╯
Only in b: teardown
Budget violations: solve, scope_profiler.session
A region present in only one file still gets a delta (treating the
missing side as 0 calls), but a percent change is only reported when the
file it is missing from is b - dropping out from a nonzero baseline in
a is a well-defined -100%, while a region appearing fresh in b has
no baseline to divide by, and is listed under “Only in b” instead.
--threshold never drops those regions, since there is nothing to
compare against.
scope-profiler plot#
Post-process one or more HDF5 profiling files and render a named plot or
plot preset. For text/JSON summaries (including LIKWID hardware
counters), see scope-profiler inspect above instead. For
machine-readable exports without rendering charts, see
scope-profiler export below.
usage: scope-profiler plot [-h]
{list,default,all,quick,gantt,flame_chart,flame_graph,
durations,timeseries,speedup,histogram,imbalance,likwid}
...
scope-profiler plot list prints the available plot kinds and presets.
Built-in Linux counter profiles can be plotted with a recorded event name or one of the derived metrics:
scope-profiler plot perf_events perf_events_example.h5 \
--metric ipc -o figures/perf_ipc.png
scope-profiler plot perf_events perf_events_example.h5 \
--metric cache-misses-per-ki -o figures/perf_cache_misses.png
Positional arguments#
Argument |
Description |
|---|---|
|
One of |
|
Path(s) or glob patterns for |
default renders gantt and the total durations plot. quick
renders durations and speedup. all renders every plot except
likwid; pass --metric to include likwid.
Selecting data#
Flag |
Description |
|---|---|
|
Override a file’s display label in the outputs (repeat once per file, in order) |
|
Region names to include (regex patterns) |
|
Region names to exclude (regex patterns) |
|
Ranks to include; supports ranges (e.g. |
Choosing and rendering plots#
Flag |
Description |
|---|---|
|
Display the plot interactively (default: off) |
|
Directory to save generated outputs; for a single plot kind this may be a target |
|
Renderer: |
|
Matplotlib colormap used to color regions/files in all plots (default: |
Plot-specific options#
Flag |
Plot(s) |
Description |
|---|---|---|
|
durations |
Duration statistics to draw as bar columns: any of |
|
durations, likwid |
Order the bar chart’s regions by this statistic, descending ( |
|
durations, likwid |
Keep only the top N regions after |
|
durations |
Merge several regions into one bar: |
|
durations |
Split each bar into the region’s own (exclusive) time plus one stacked segment per region called directly from it, so a bar shows where its time went. Nesting is reconstructed from timestamps, as in the flame chart; children filtered out of the bars still get a segment. Only |
|
durations, timeseries, histogram, imbalance, likwid |
Logarithmic y-axis |
|
histogram |
Number of duration bins (default: 30) |
|
imbalance |
Per-call duration statistic plotted per rank: any of |
|
likwid |
Name of the LIKWID derived metric or raw event to plot, e.g. |
|
speedup, weak_scaling, weak_scaling_efficiency, scaling_efficiency |
X-axis: |
|
weak_scaling_efficiency |
Work per rank in each file, in the order the files are given, in any unit (grid cells, particles, unknowns). Checked to be equal across the files: weak-scaling efficiency compares runs that each do the same work, so unequal values are an error rather than a misleading curve |
When -o/--output is supplied, the CLI saves one <name>_plot.png per
plot selected by the plot kind or preset (durations_plot.png becomes
one durations_plot_<metric>.png per metric when --metrics requests
several, e.g. durations_plot_avg.png, durations_plot_total.png),
plus region_statistics.json. speedup is skipped unless multiple
files are passed. With --backend plotly the plots are written as
.html instead of .png.
The plotext backend is available for simple plots (durations,
timeseries, speedup, weak_scaling, weak_scaling_efficiency,
scaling_efficiency, histogram, and imbalance). It writes plain
terminal renderings as .txt files. Complex plots such as Gantt charts,
flame charts, flame graphs, heatmaps, and LIKWID charts remain on the
Matplotlib or Plotly backends.
For multiple files, the JSON includes per-file region statistics and the set of common regions across all inputs.
scope-profiler export#
Export profiling data without rendering plot images.
usage: scope-profiler export [-h] {prof,speedscope,chrome-trace,json,plot-data} ...
Plot data#
plot-data writes the raw data behind selected charts as CSV or JSON,
so plots can be reconstructed later without the original HDF5 files:
scope-profiler export plot-data profiling_data.h5 -o data --format json
scope-profiler export plot-data run_1.h5 run_2.h5 -o data \
--plots durations speedup --format json
With JSON, each data file includes a colors map matching the plot
colors. plot-data supports the same data-selection flags as plot,
plus --plots, --format, --metrics, --bins, --imbalance-metric,
--likwid-metric, and --x.
Exporting to .prof for snakeviz#
export prof writes the profile in the format cProfile uses, so
regions can be browsed with any pstats-based viewer:
scope-profiler export prof profiling_data.h5 -o figures
snakeviz figures/profile_rank0.prof
One file is written per exported rank (profile_rank0.prof, …; only the
ranks selected with -r/--ranks are exported, default rank 0), prefixed
with the input file’s stem when several HDF5 files are passed. Regions
become “functions”, cumtime is a region’s total wall time and
tottime is that minus the time spent in its nested regions, with a
synthetic <file rank N> frame as the root of the tree. Regions that
captured a Python source location are keyed by it, so viewers link back
to the source line.
Since regions carry no call graph, the caller/callee relations are reconstructed from timestamp containment, exactly as the flame chart does. So:
By default each distinct call path is its own entry, named
parent > child, so a region used below two different parents stays two nodes in the tree. Pass--no-call-pathsto merge every call of a region into one entry instead, which is how pstats keys a realcProfilerun; recursion is then reported as2/1-style call counts, likecProfile.Regions must nest: intervals that only partially overlap (async work, threads) are rejected with a
NestingErrorrather than being silently attributed to whichever region enclosed their start.LIKWID counters have no place in the
.profformat and are left out.
The same data is available in memory, without writing a file, through
scope_profiler.to_pstats(), which returns a pstats.Stats per rank.
Exporting a flame graph as SVG#
export flamegraph renders the same reconstructed call tree as a
standalone SVG, through flameprof
(part of the pproc extra):
scope-profiler export flamegraph profiling_data.h5 -o figures
This is the aggregated view — every call of a region below one parent is
a single frame, sized by cumulative time — the same thing
plot flame_graph draws, but as a self-contained document with no
JavaScript and no plotting stack, which is what a static site or an
emailed report wants. Hovering a frame reports its percentage, call
count, tottime and cumtime.
One file is written per exported rank (flamegraph_rank0.svg, …; only
the ranks selected with -r/--ranks, default rank 0), prefixed with the
input file’s stem when several HDF5 files are passed. --no-call-paths
merges every call of a region into one frame, as for export prof.
By default the SVG carries a viewBox rather than a fixed pixel size,
and no XML prolog, so a page can inline it and scale it to whatever
element holds it — inlining being the only way the per-frame tooltips
survive, since an <img> renders the SVG without its hover handlers.
Pass --fixed-width for a standalone file instead, --width to change
the aspect ratio and the point at which frame labels are elided, and
--threshold to drop frames below a share of total time (default:
0.1%).
The same rendering is available from Python as
scope_profiler.export_flamegraph_svg().
Exporting to JSON#
export json writes the whole run as a JSON document — the same data
the HDF5 file holds, in a format anything can read:
scope-profiler export json profiling_data.h5 -o exports
scope-profiler export json profiling_data.h5 -o exports --gzip --indent 2
Unlike prof and speedscope, this is a lossless copy rather than a
view for a particular viewer: every rank is exported by default
(-r/--ranks narrows it), timestamps stay in the integer nanoseconds
the run recorded, and scope_profiler.read_json() rebuilds a
ProfilingResults indistinguishable from the one read_h5() gives
back. --gzip writes profile.json.gz, which is what a real run wants:
the event columns are the bulk of the document and compress by roughly
an order of magnitude. --indent N pretty-prints it for reading by eye
or diffing in review. One file is written per input file, with the run’s
label added to the name when several are passed.
Exporting to speedscope#
export speedscope writes a speedscope
JSON file. Where .prof keeps aggregates per region, speedscope keeps
every individual call, so the timeline shows the run as it actually
happened — closest in spirit to the Gantt and flame charts, but
interactive:
scope-profiler export speedscope profiling_data.h5 -o figures
Then open figures/profile.speedscope.json at
https://www.speedscope.app (the file never leaves the browser), or run
npx speedscope figures/profile.speedscope.json. Its three views are
all useful here: “Time Order” is the run’s timeline, “Left Heavy”
aggregates identical call paths (the flame graph), and “Sandwich” ranks
regions by self and total time.
One file is written per input HDF5 file, holding one profile per
exported rank (only the ranks selected with -r/--ranks, default rank
0) — the format carries several profiles per file, and speedscope
switches between them from the dropdown in its top bar. Every profile in
a file shares one time origin, so ranks stay aligned with each other.
The stem of the input file is added to the name when several HDF5 files
are passed.
Exporting to Chrome Trace / Perfetto#
export chrome-trace writes Chrome Trace Event JSON. Each rank is a
process; tracked threads and asyncio tasks become separate lanes. Region
durations are complete events, with GPU and await measurements in event
arguments. Aggregate Linux perf-event values are emitted as counter
events at the end of the rank timeline.
scope-profiler export chrome-trace profiling_data.h5 -o figures
Open figures/profile.trace.json in Perfetto
or chrome://tracing. The export is dependency-free and does not
require the optional plotting dependencies.
The call graph is reconstructed from timestamp containment, as for
.prof, with one extra consequence: speedscope replays the events as a
stack machine, so a region that starts inside another but ends after it
is clipped to its parent instead of overhanging it. The same caveats
otherwise apply — regions called from several places and recursion
behave as described above.
Examples#
Save plots for a single file:
scope-profiler plot profiling_data.h5 -o figures/
Omitting the plot kind selects the default preset (gantt and total
durations). The explicit equivalent is
scope-profiler plot default profiling_data.h5 -o figures/.
Compare multiple files:
scope-profiler plot default run_1.h5 run_2.h5 run_4.h5 -o figures/
Select files via wildcard patterns:
scope-profiler plot default files/*.h5 -o figures/
scope-profiler plot default "files/file_*.h5" -o figures/
Display interactively with region filtering:
scope-profiler plot profiling_data.h5 --show \
--include "solver.*" "rhs.*" \
--exclude "io"
Select specific MPI ranks:
scope-profiler plot default profiling_data.h5 --show --ranks 0-3 8
The --ranks flag accepts comma-separated values and dash ranges that
can be combined: 0,2,4-7 expands to ranks 0, 2, 4, 5, 6, 7.
Only export average and total duration plots:
scope-profiler plot durations profiling_data.h5 -o figures/ --metrics avg total
Only generate the duration bar chart and speedup plot:
scope-profiler plot quick run_1.h5 run_2.h5 run_4.h5 -o figures/