scope-profiler 0.2__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scope_profiler-0.2 → scope_profiler-0.2.2}/PKG-INFO +162 -19
- {scope_profiler-0.2 → scope_profiler-0.2.2}/README.md +159 -18
- {scope_profiler-0.2 → scope_profiler-0.2.2}/pyproject.toml +3 -1
- scope_profiler-0.2.2/src/scope_profiler/__init__.py +49 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/__main__.py +20 -3
- scope_profiler-0.2.2/src/scope_profiler/h5reader.py +380 -0
- scope_profiler-0.2.2/src/scope_profiler/inspection.py +373 -0
- scope_profiler-0.2.2/src/scope_profiler/metadata.py +200 -0
- scope_profiler-0.2.2/src/scope_profiler/mpi_region.py +289 -0
- scope_profiler-0.2.2/src/scope_profiler/plotting_scripts.py +1157 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/post_processing.py +122 -5
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/profile_config.py +16 -3
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/profile_manager.py +59 -73
- scope_profiler-0.2.2/src/scope_profiler/region.py +159 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/region_profiler.py +102 -192
- scope_profiler-0.2.2/src/scope_profiler/summary.py +196 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_app.py +156 -13
- scope_profiler-0.2.2/src/scope_profiler/tests/test_buffer_growth.py +126 -0
- scope_profiler-0.2.2/src/scope_profiler/tests/test_inspection.py +318 -0
- scope_profiler-0.2.2/src/scope_profiler/tests/test_metadata.py +175 -0
- scope_profiler-0.2.2/src/scope_profiler/tests/test_post_processing.py +516 -0
- scope_profiler-0.2.2/src/scope_profiler/tests/test_reader_api.py +204 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/PKG-INFO +162 -19
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/SOURCES.txt +7 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/requires.txt +2 -0
- scope_profiler-0.2/src/scope_profiler/__init__.py +0 -7
- scope_profiler-0.2/src/scope_profiler/h5reader.py +0 -189
- scope_profiler-0.2/src/scope_profiler/mpi_region.py +0 -117
- scope_profiler-0.2/src/scope_profiler/plotting_scripts.py +0 -850
- scope_profiler-0.2/src/scope_profiler/region.py +0 -112
- scope_profiler-0.2/src/scope_profiler/tests/test_post_processing.py +0 -221
- {scope_profiler-0.2 → scope_profiler-0.2.2}/setup.cfg +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/__init__.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/examples.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/examples_pylikwid.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/pylikwid_readme.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_mpi.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_overhead.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_readme.py +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/dependency_links.txt +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/entry_points.txt +0 -0
- {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scope-profiler
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Profile code regions in python, optionally with LIKWID markers.
|
|
5
5
|
Author: Max
|
|
6
6
|
Project-URL: Source, https://github.com/max-models/scope-profiler
|
|
@@ -23,6 +23,8 @@ Provides-Extra: pproc
|
|
|
23
23
|
Requires-Dist: ipykernel; extra == "pproc"
|
|
24
24
|
Requires-Dist: jupyterlab; extra == "pproc"
|
|
25
25
|
Requires-Dist: matplotlib; extra == "pproc"
|
|
26
|
+
Requires-Dist: maxplotlibx>=0.1.5; extra == "pproc"
|
|
27
|
+
Requires-Dist: pandas; extra == "pproc"
|
|
26
28
|
Provides-Extra: dev
|
|
27
29
|
Requires-Dist: black[jupyter]; extra == "dev"
|
|
28
30
|
Requires-Dist: isort; extra == "dev"
|
|
@@ -97,22 +99,81 @@ Execution:
|
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
101
|
❯ python test.py
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
102
|
+
profiling_data.h5 (1 rank(s))
|
|
103
|
+
region ranks calls total [s] avg [s] min [s] max [s] std [s]
|
|
104
|
+
----------------------------------------------------------------------------------------
|
|
105
|
+
main 1 1 0.00150371 0.00150371 0.00150371 0.00150371 0
|
|
106
|
+
iteration 1 10 3.832e-06 3.832e-07 2.08e-07 8.75e-07 2.24319e-07
|
|
107
|
+
----------------------------------------------------------------------------------------
|
|
108
|
+
TOTAL 11 0.00150754
|
|
109
|
+
|
|
110
|
+
Regions may nest, so the summed total can exceed the wall-clock time.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`finalize()` prints the same table as `scope-profiler inspect` and
|
|
114
|
+
`ProfilingH5Reader.print_summary()`. Pass `verbose=False` to suppress it.
|
|
115
|
+
|
|
116
|
+
## Inspecting a profiling file
|
|
117
|
+
|
|
118
|
+
`scope-profiler inspect` prints what is inside an HDF5 profiling file: the
|
|
119
|
+
full run metadata (host, CPU, loaded modules, Slurm job, environment) and one
|
|
120
|
+
statistics line per region, with no plotting dependencies needed.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
scope-profiler inspect profiling_data.h5
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
==============================================================================
|
|
128
|
+
profiling_data.h5
|
|
129
|
+
2 rank(s), 4 region(s), 0.18 MiB, 0.0951538 s wall clock
|
|
130
|
+
==============================================================================
|
|
131
|
+
|
|
132
|
+
Metadata
|
|
133
|
+
Run
|
|
134
|
+
timestamp : 2026-07-26T18:57:49
|
|
135
|
+
user : mlindqvi
|
|
136
|
+
hostname : lrdn1234
|
|
137
|
+
System
|
|
138
|
+
chip_information : AMD EPYC 9654 96-Core Processor
|
|
139
|
+
Parallelism
|
|
140
|
+
mpi_size : 2
|
|
141
|
+
omp_num_threads : 8
|
|
142
|
+
total_cores : 16
|
|
143
|
+
Slurm
|
|
144
|
+
SLURM_JOB_ID : 9988776
|
|
145
|
+
Modules (4)
|
|
146
|
+
profile/base
|
|
147
|
+
gcc/12.3.0
|
|
148
|
+
openmpi/4.1.6--gcc--12.3.0
|
|
149
|
+
python/3.11.7
|
|
150
|
+
|
|
151
|
+
Regions (4)
|
|
152
|
+
region ranks calls total [s] avg [s] min [s] max [s] std [s]
|
|
153
|
+
--------------------------------------------------------------------------------
|
|
154
|
+
timestep 2 8 0.139235 0.0174044 0.0165256 0.0176325 0.000338551
|
|
155
|
+
solve 2 8 0.0991292 0.0123911 0.0115046 0.0125382 0.000335212
|
|
156
|
+
setup 2 2 0.0473326 0.0236663 0.0222938 0.0250388 0.00137254
|
|
157
|
+
assemble 2 8 0.0399496 0.0049937 0.00484729 0.0050345 5.5882e-05
|
|
158
|
+
--------------------------------------------------------------------------------
|
|
159
|
+
TOTAL 26 0.325647
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Long values such as `PATH` are clipped unless `--full` is passed, regions can
|
|
163
|
+
be filtered with `--include`/`--exclude`/`--ranks`, reordered with `--sort`,
|
|
164
|
+
and either section shown alone with `--metadata-only` / `--regions-only`.
|
|
165
|
+
|
|
166
|
+
The metadata can also be exported to JSON, with one entry per inspected file
|
|
167
|
+
and no clipping:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from scope_profiler.inspection import write_metadata_json
|
|
175
|
+
|
|
176
|
+
write_metadata_json("profiling_data.h5", "metadata.json")
|
|
116
177
|
```
|
|
117
178
|
|
|
118
179
|
## Example plots
|
|
@@ -245,6 +306,48 @@ ProfileManager.finalize()
|
|
|
245
306
|
Both `fibonacci` and `fibonacci_ctx` will report one call per recursive
|
|
246
307
|
invocation, each with correct, non-overlapping timing data.
|
|
247
308
|
|
|
309
|
+
## Analysing results in Python
|
|
310
|
+
|
|
311
|
+
`ProfilingH5Reader` loads a merged profiling file and behaves like an ordered
|
|
312
|
+
mapping of region name to region. Every duration and timestamp it reports is in
|
|
313
|
+
**seconds**:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from scope_profiler import ProfilingH5Reader
|
|
317
|
+
|
|
318
|
+
reader = ProfilingH5Reader("profiling_data.h5")
|
|
319
|
+
reader.print_summary()
|
|
320
|
+
|
|
321
|
+
# region calls total [s] avg [s] min [s] max [s]
|
|
322
|
+
# ---------------------------------------------------------------------------
|
|
323
|
+
# setup 1 0.02401 0.02401 0.02401 0.02401
|
|
324
|
+
# timestep 5 0.062835 0.012567 0.0087755 0.0187844
|
|
325
|
+
|
|
326
|
+
solve = reader["solve"] # an MPIRegion: the region across all ranks
|
|
327
|
+
solve.num_calls # summed over ranks
|
|
328
|
+
solve.total_duration # seconds
|
|
329
|
+
solve.average_durations() # {rank: seconds}, for load imbalance
|
|
330
|
+
solve[0].durations # every call on rank 0, as a numpy array
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`summary()` returns the same table as a list of dicts, and `to_dataframe()`
|
|
334
|
+
returns it as a pandas DataFrame (one row per region, or per region and rank
|
|
335
|
+
with `per_rank=True`):
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
frame = reader.to_dataframe().sort_values("total_duration", ascending=False)
|
|
339
|
+
per_rank = reader.to_dataframe(per_rank=True)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
`include` / `exclude` regexes select regions in `get_regions()`, `summary()`,
|
|
343
|
+
`to_dataframe()` and every `plot_*` function.
|
|
344
|
+
|
|
345
|
+
The [tutorial notebooks](tutorials/) cover this in depth:
|
|
346
|
+
[getting started](tutorials/01_getting_started.ipynb),
|
|
347
|
+
[post-processing](tutorials/02_postprocessing.ipynb),
|
|
348
|
+
[visualization](tutorials/03_visualization.ipynb) and
|
|
349
|
+
[profiling modes](tutorials/04_profiling_modes.ipynb).
|
|
350
|
+
|
|
248
351
|
## Flame graphs
|
|
249
352
|
|
|
250
353
|
Because each call - including recursive re-entries of the same region -
|
|
@@ -265,8 +368,7 @@ scope-profiler pproc profiling_data.h5 --show -o figures
|
|
|
265
368
|
Or programmatically:
|
|
266
369
|
|
|
267
370
|
```python
|
|
268
|
-
from scope_profiler
|
|
269
|
-
from scope_profiler.plotting_scripts import plot_flame
|
|
371
|
+
from scope_profiler import ProfilingH5Reader, plot_flame
|
|
270
372
|
|
|
271
373
|
reader = ProfilingH5Reader("profiling_data.h5")
|
|
272
374
|
plot_flame(reader, filepath="flame_plot.png")
|
|
@@ -284,3 +386,44 @@ scope-profiler pproc profiling_data.h5 --cmap viridis -o figures
|
|
|
284
386
|
By default the flame graph covers rank 0, since it represents a single
|
|
285
387
|
execution's call stack; pass `ranks=[...]` to render one flame graph per
|
|
286
388
|
requested rank.
|
|
389
|
+
|
|
390
|
+
## Exporting plot data
|
|
391
|
+
|
|
392
|
+
Every `plot_*` function accepts a `data_filepath` argument that writes the
|
|
393
|
+
exact data behind the chart to a file, so it can be re-parsed and re-plotted
|
|
394
|
+
later without the original HDF5 file. `data_format` selects `"csv"` (default)
|
|
395
|
+
or `"json"`:
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
plot_gantt(reader, filepath="gantt_plot.png", data_filepath="gantt_data.csv")
|
|
399
|
+
plot_gantt(
|
|
400
|
+
reader,
|
|
401
|
+
filepath="gantt_plot.png",
|
|
402
|
+
data_filepath="gantt_data.json",
|
|
403
|
+
data_format="json",
|
|
404
|
+
)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The JSON payload additionally includes a `colors` map (region or file label
|
|
408
|
+
to `#rrggbb`) matching the colors used in the matplotlib plot, so a
|
|
409
|
+
JavaScript charting library like Plotly can reproduce the same look.
|
|
410
|
+
|
|
411
|
+
`scope-profiler pproc --export-data` does the same for every plot in one
|
|
412
|
+
run, writing `gantt_data`, `flame_data`, `durations_data`, and (for multiple
|
|
413
|
+
input files) `speedup_data` alongside the PNGs. Pass `--export-data-format
|
|
414
|
+
json` to get `.json` files instead of the default `.csv`:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
scope-profiler pproc profiling_data.h5 -o figures --export-data
|
|
418
|
+
scope-profiler pproc profiling_data.h5 -o figures --export-data --export-data-format json
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Pass `--skip-plot-images` (requires `--export-data`) to skip rendering the
|
|
422
|
+
PNGs entirely and only write the exported data plus `region_statistics.json`
|
|
423
|
+
— useful when a website renders charts client-side (e.g. with Plotly)
|
|
424
|
+
straight from the JSON:
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
scope-profiler pproc profiling_data.h5 -o figures \
|
|
428
|
+
--export-data --export-data-format json --skip-plot-images
|
|
429
|
+
```
|
|
@@ -54,22 +54,81 @@ Execution:
|
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
❯ python test.py
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
profiling_data.h5 (1 rank(s))
|
|
58
|
+
region ranks calls total [s] avg [s] min [s] max [s] std [s]
|
|
59
|
+
----------------------------------------------------------------------------------------
|
|
60
|
+
main 1 1 0.00150371 0.00150371 0.00150371 0.00150371 0
|
|
61
|
+
iteration 1 10 3.832e-06 3.832e-07 2.08e-07 8.75e-07 2.24319e-07
|
|
62
|
+
----------------------------------------------------------------------------------------
|
|
63
|
+
TOTAL 11 0.00150754
|
|
64
|
+
|
|
65
|
+
Regions may nest, so the summed total can exceed the wall-clock time.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`finalize()` prints the same table as `scope-profiler inspect` and
|
|
69
|
+
`ProfilingH5Reader.print_summary()`. Pass `verbose=False` to suppress it.
|
|
70
|
+
|
|
71
|
+
## Inspecting a profiling file
|
|
72
|
+
|
|
73
|
+
`scope-profiler inspect` prints what is inside an HDF5 profiling file: the
|
|
74
|
+
full run metadata (host, CPU, loaded modules, Slurm job, environment) and one
|
|
75
|
+
statistics line per region, with no plotting dependencies needed.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
scope-profiler inspect profiling_data.h5
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
==============================================================================
|
|
83
|
+
profiling_data.h5
|
|
84
|
+
2 rank(s), 4 region(s), 0.18 MiB, 0.0951538 s wall clock
|
|
85
|
+
==============================================================================
|
|
86
|
+
|
|
87
|
+
Metadata
|
|
88
|
+
Run
|
|
89
|
+
timestamp : 2026-07-26T18:57:49
|
|
90
|
+
user : mlindqvi
|
|
91
|
+
hostname : lrdn1234
|
|
92
|
+
System
|
|
93
|
+
chip_information : AMD EPYC 9654 96-Core Processor
|
|
94
|
+
Parallelism
|
|
95
|
+
mpi_size : 2
|
|
96
|
+
omp_num_threads : 8
|
|
97
|
+
total_cores : 16
|
|
98
|
+
Slurm
|
|
99
|
+
SLURM_JOB_ID : 9988776
|
|
100
|
+
Modules (4)
|
|
101
|
+
profile/base
|
|
102
|
+
gcc/12.3.0
|
|
103
|
+
openmpi/4.1.6--gcc--12.3.0
|
|
104
|
+
python/3.11.7
|
|
105
|
+
|
|
106
|
+
Regions (4)
|
|
107
|
+
region ranks calls total [s] avg [s] min [s] max [s] std [s]
|
|
108
|
+
--------------------------------------------------------------------------------
|
|
109
|
+
timestep 2 8 0.139235 0.0174044 0.0165256 0.0176325 0.000338551
|
|
110
|
+
solve 2 8 0.0991292 0.0123911 0.0115046 0.0125382 0.000335212
|
|
111
|
+
setup 2 2 0.0473326 0.0236663 0.0222938 0.0250388 0.00137254
|
|
112
|
+
assemble 2 8 0.0399496 0.0049937 0.00484729 0.0050345 5.5882e-05
|
|
113
|
+
--------------------------------------------------------------------------------
|
|
114
|
+
TOTAL 26 0.325647
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Long values such as `PATH` are clipped unless `--full` is passed, regions can
|
|
118
|
+
be filtered with `--include`/`--exclude`/`--ranks`, reordered with `--sort`,
|
|
119
|
+
and either section shown alone with `--metadata-only` / `--regions-only`.
|
|
120
|
+
|
|
121
|
+
The metadata can also be exported to JSON, with one entry per inspected file
|
|
122
|
+
and no clipping:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from scope_profiler.inspection import write_metadata_json
|
|
130
|
+
|
|
131
|
+
write_metadata_json("profiling_data.h5", "metadata.json")
|
|
73
132
|
```
|
|
74
133
|
|
|
75
134
|
## Example plots
|
|
@@ -202,6 +261,48 @@ ProfileManager.finalize()
|
|
|
202
261
|
Both `fibonacci` and `fibonacci_ctx` will report one call per recursive
|
|
203
262
|
invocation, each with correct, non-overlapping timing data.
|
|
204
263
|
|
|
264
|
+
## Analysing results in Python
|
|
265
|
+
|
|
266
|
+
`ProfilingH5Reader` loads a merged profiling file and behaves like an ordered
|
|
267
|
+
mapping of region name to region. Every duration and timestamp it reports is in
|
|
268
|
+
**seconds**:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from scope_profiler import ProfilingH5Reader
|
|
272
|
+
|
|
273
|
+
reader = ProfilingH5Reader("profiling_data.h5")
|
|
274
|
+
reader.print_summary()
|
|
275
|
+
|
|
276
|
+
# region calls total [s] avg [s] min [s] max [s]
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
# setup 1 0.02401 0.02401 0.02401 0.02401
|
|
279
|
+
# timestep 5 0.062835 0.012567 0.0087755 0.0187844
|
|
280
|
+
|
|
281
|
+
solve = reader["solve"] # an MPIRegion: the region across all ranks
|
|
282
|
+
solve.num_calls # summed over ranks
|
|
283
|
+
solve.total_duration # seconds
|
|
284
|
+
solve.average_durations() # {rank: seconds}, for load imbalance
|
|
285
|
+
solve[0].durations # every call on rank 0, as a numpy array
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`summary()` returns the same table as a list of dicts, and `to_dataframe()`
|
|
289
|
+
returns it as a pandas DataFrame (one row per region, or per region and rank
|
|
290
|
+
with `per_rank=True`):
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
frame = reader.to_dataframe().sort_values("total_duration", ascending=False)
|
|
294
|
+
per_rank = reader.to_dataframe(per_rank=True)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`include` / `exclude` regexes select regions in `get_regions()`, `summary()`,
|
|
298
|
+
`to_dataframe()` and every `plot_*` function.
|
|
299
|
+
|
|
300
|
+
The [tutorial notebooks](tutorials/) cover this in depth:
|
|
301
|
+
[getting started](tutorials/01_getting_started.ipynb),
|
|
302
|
+
[post-processing](tutorials/02_postprocessing.ipynb),
|
|
303
|
+
[visualization](tutorials/03_visualization.ipynb) and
|
|
304
|
+
[profiling modes](tutorials/04_profiling_modes.ipynb).
|
|
305
|
+
|
|
205
306
|
## Flame graphs
|
|
206
307
|
|
|
207
308
|
Because each call - including recursive re-entries of the same region -
|
|
@@ -222,8 +323,7 @@ scope-profiler pproc profiling_data.h5 --show -o figures
|
|
|
222
323
|
Or programmatically:
|
|
223
324
|
|
|
224
325
|
```python
|
|
225
|
-
from scope_profiler
|
|
226
|
-
from scope_profiler.plotting_scripts import plot_flame
|
|
326
|
+
from scope_profiler import ProfilingH5Reader, plot_flame
|
|
227
327
|
|
|
228
328
|
reader = ProfilingH5Reader("profiling_data.h5")
|
|
229
329
|
plot_flame(reader, filepath="flame_plot.png")
|
|
@@ -241,3 +341,44 @@ scope-profiler pproc profiling_data.h5 --cmap viridis -o figures
|
|
|
241
341
|
By default the flame graph covers rank 0, since it represents a single
|
|
242
342
|
execution's call stack; pass `ranks=[...]` to render one flame graph per
|
|
243
343
|
requested rank.
|
|
344
|
+
|
|
345
|
+
## Exporting plot data
|
|
346
|
+
|
|
347
|
+
Every `plot_*` function accepts a `data_filepath` argument that writes the
|
|
348
|
+
exact data behind the chart to a file, so it can be re-parsed and re-plotted
|
|
349
|
+
later without the original HDF5 file. `data_format` selects `"csv"` (default)
|
|
350
|
+
or `"json"`:
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
plot_gantt(reader, filepath="gantt_plot.png", data_filepath="gantt_data.csv")
|
|
354
|
+
plot_gantt(
|
|
355
|
+
reader,
|
|
356
|
+
filepath="gantt_plot.png",
|
|
357
|
+
data_filepath="gantt_data.json",
|
|
358
|
+
data_format="json",
|
|
359
|
+
)
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The JSON payload additionally includes a `colors` map (region or file label
|
|
363
|
+
to `#rrggbb`) matching the colors used in the matplotlib plot, so a
|
|
364
|
+
JavaScript charting library like Plotly can reproduce the same look.
|
|
365
|
+
|
|
366
|
+
`scope-profiler pproc --export-data` does the same for every plot in one
|
|
367
|
+
run, writing `gantt_data`, `flame_data`, `durations_data`, and (for multiple
|
|
368
|
+
input files) `speedup_data` alongside the PNGs. Pass `--export-data-format
|
|
369
|
+
json` to get `.json` files instead of the default `.csv`:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
scope-profiler pproc profiling_data.h5 -o figures --export-data
|
|
373
|
+
scope-profiler pproc profiling_data.h5 -o figures --export-data --export-data-format json
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Pass `--skip-plot-images` (requires `--export-data`) to skip rendering the
|
|
377
|
+
PNGs entirely and only write the exported data plus `region_statistics.json`
|
|
378
|
+
— useful when a website renders charts client-side (e.g. with Plotly)
|
|
379
|
+
straight from the JSON:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
scope-profiler pproc profiling_data.h5 -o figures \
|
|
383
|
+
--export-data --export-data-format json --skip-plot-images
|
|
384
|
+
```
|
|
@@ -5,7 +5,7 @@ requires = [ "setuptools", "wheel" ]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "scope-profiler"
|
|
8
|
-
version = "0.2"
|
|
8
|
+
version = "0.2.2"
|
|
9
9
|
description = "Profile code regions in python, optionally with LIKWID markers."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = [ "python" ]
|
|
@@ -35,6 +35,8 @@ optional-dependencies.pproc = [
|
|
|
35
35
|
"ipykernel",
|
|
36
36
|
"jupyterlab",
|
|
37
37
|
"matplotlib",
|
|
38
|
+
"maxplotlibx >= 0.1.5",
|
|
39
|
+
"pandas",
|
|
38
40
|
]
|
|
39
41
|
|
|
40
42
|
optional-dependencies.dev = [
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""scope-profiler: lightweight region-based profiling for Python and HPC applications."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from scope_profiler.h5reader import ProfilingH5Reader
|
|
6
|
+
from scope_profiler.mpi_region import MPIRegion
|
|
7
|
+
from scope_profiler.profile_manager import ProfileManager
|
|
8
|
+
from scope_profiler.region import Region
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("scope-profiler")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
__version__ = "unknown"
|
|
14
|
+
|
|
15
|
+
# Plotting pulls in the optional maxplotlib stack, so these are resolved on
|
|
16
|
+
# first access (PEP 562) rather than at import time, keeping
|
|
17
|
+
# `import scope_profiler` cheap inside the applications being profiled.
|
|
18
|
+
_LAZY_PLOTTING = frozenset(
|
|
19
|
+
{
|
|
20
|
+
"collect_region_statistics",
|
|
21
|
+
"plot_durations",
|
|
22
|
+
"plot_flame",
|
|
23
|
+
"plot_gantt",
|
|
24
|
+
"plot_speedup",
|
|
25
|
+
"write_region_statistics_json",
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"MPIRegion",
|
|
31
|
+
"ProfileManager",
|
|
32
|
+
"ProfilingH5Reader",
|
|
33
|
+
"Region",
|
|
34
|
+
*sorted(_LAZY_PLOTTING),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def __getattr__(name: str):
|
|
39
|
+
"""Resolve the plotting helpers lazily; see ``_LAZY_PLOTTING``."""
|
|
40
|
+
if name in _LAZY_PLOTTING:
|
|
41
|
+
from scope_profiler import plotting_scripts
|
|
42
|
+
|
|
43
|
+
return getattr(plotting_scripts, name)
|
|
44
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def __dir__() -> list:
|
|
48
|
+
"""Include the lazily-resolved plotting helpers in ``dir()``."""
|
|
49
|
+
return sorted(set(globals()) | _LAZY_PLOTTING)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Also runnable as ``python -m scope_profiler <command> ...``.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Three subcommands:
|
|
6
6
|
|
|
7
7
|
- ``scope-profiler run script.py [args...]`` -- profiles a script's function
|
|
8
8
|
calls without requiring any decorators or context managers in the script
|
|
@@ -12,6 +12,9 @@ Two subcommands:
|
|
|
12
12
|
- ``scope-profiler pproc file.h5 [...]`` -- reads merged HDF5 profiling
|
|
13
13
|
output and renders Gantt/flame/duration/speedup charts. See
|
|
14
14
|
``scope_profiler.post_processing`` for its full set of options.
|
|
15
|
+
- ``scope-profiler inspect file.h5 [...]`` -- prints the run metadata and a
|
|
16
|
+
per-region statistics table for merged HDF5 profiling output, without
|
|
17
|
+
producing any plots. See ``scope_profiler.inspection``.
|
|
15
18
|
"""
|
|
16
19
|
|
|
17
20
|
import argparse
|
|
@@ -47,8 +50,8 @@ def _parse_run_args(argv):
|
|
|
47
50
|
parser.add_argument(
|
|
48
51
|
"--buffer-limit",
|
|
49
52
|
type=int,
|
|
50
|
-
default=
|
|
51
|
-
help="
|
|
53
|
+
default=1024,
|
|
54
|
+
help="Initial buffer capacity per region; grows as needed (default: 1024)",
|
|
52
55
|
)
|
|
53
56
|
parser.add_argument("script", help="Script to run and profile")
|
|
54
57
|
parser.add_argument(
|
|
@@ -97,9 +100,17 @@ def _pproc(argv):
|
|
|
97
100
|
return pproc_main(argv)
|
|
98
101
|
|
|
99
102
|
|
|
103
|
+
def _inspect(argv):
|
|
104
|
+
"""Handle ``scope-profiler inspect``: delegate to the inspection CLI."""
|
|
105
|
+
from scope_profiler.inspection import main as inspect_main
|
|
106
|
+
|
|
107
|
+
return inspect_main(argv)
|
|
108
|
+
|
|
109
|
+
|
|
100
110
|
_COMMANDS = {
|
|
101
111
|
"run": _run,
|
|
102
112
|
"pproc": _pproc,
|
|
113
|
+
"inspect": _inspect,
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
|
|
@@ -123,6 +134,12 @@ def main(argv=None):
|
|
|
123
134
|
help="Post-process and plot HDF5 profiling data "
|
|
124
135
|
"(see `scope-profiler pproc --help`)",
|
|
125
136
|
)
|
|
137
|
+
subparsers.add_parser(
|
|
138
|
+
"inspect",
|
|
139
|
+
add_help=False,
|
|
140
|
+
help="Print metadata and region statistics of HDF5 profiling data "
|
|
141
|
+
"(see `scope-profiler inspect --help`)",
|
|
142
|
+
)
|
|
126
143
|
|
|
127
144
|
if not argv:
|
|
128
145
|
parser.print_help()
|