carm-roofline 1.0.2.dev0__tar.gz → 1.0.2.dev2__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.
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/PKG-INFO +15 -4
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/README.md +14 -3
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/__init__.py +8 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/identity.py +115 -13
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/arithmetic.py +6 -6
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/jsonl.py +12 -12
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/memory.py +2 -2
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/memory_sweep.py +5 -5
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/roofline.py +6 -8
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/memory_sweep.py +3 -3
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm.py +5 -2
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/PKG-INFO +15 -4
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/assets/style.css +16 -42
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/components.py +7 -39
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/data.py +33 -21
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/factory.py +13 -56
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/ids.py +2 -10
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/output_utils.py +13 -20
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/__init__.py +2 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/aggregation.py +98 -43
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/config.py +5 -11
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/output.py +6 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/shared.py +24 -16
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/pyproject.toml +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_gui_data.py +69 -17
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test_bench/test_bench.c +2 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test_bench/test_bench.h +2 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test_bench/wrapper.inl +7 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/units.py +8 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/LICENSE +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/architecture.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/arm.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/detect.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/frequency.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/memory.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/riscv.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/arm/features.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/arm/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/arm/vlen.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/frequency.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/riscv/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/riscv/rvv_version.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/riscv/vlen.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/x86/cache.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/x86/features.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/x86/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/tests/x86/x86_avx512/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/architecture/x86.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/arguments.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/benchmark.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/benchmarking.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/arm.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/code_gen/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/code_gen/data_type.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/code_gen/instruction.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/code_gen/operation.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/code_gen/register.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/isa.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/parameters.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/riscv.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/generation/x86.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/interface.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/base.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/common.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/output/mixed.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/result.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/arithmetic.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/base.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/memory.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/mixed.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/benchmark/suites/roofline.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/SOURCES.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/dependency_links.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/entry_points.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/requires.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/carm_roofline.egg-info/top_level.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/context.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/error.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/exec_interface.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/assets/CARM_icon.svg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/assets/CHAMP_logo.svg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/assets/menu_icon.png +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/gui/config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/backends.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/model.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/papi_backend.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/papi_loader.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/papi_metrics.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/perf_backend.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/perf_loader.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/profiling/perf_metrics.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/results_paths.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/roofline_assembly.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/run_config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/setup.cfg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_isa_codegen_integration.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_memory_suite_generation.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_output_module.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_roofline_assembly.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test/test_typed_benchmarks.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test_bench/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/test_bench/builder.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/utils.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev2}/workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-roofline
|
|
3
|
-
Version: 1.0.2.
|
|
3
|
+
Version: 1.0.2.dev2
|
|
4
4
|
Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
|
|
5
5
|
Author: CHaMP Team
|
|
6
6
|
License: Apache License
|
|
@@ -246,7 +246,6 @@ Dynamic: license-file
|
|
|
246
246
|
|
|
247
247
|
# The CARM Tool
|
|
248
248
|
|
|
249
|
-
|
|
250
249
|
Cache-Aware Roofline Model (CARM) toolkit - benchmark, visualize and profile Intel, AMD, ARM, RISC-V CPUs and NVIDIA, AMD GPUs.
|
|
251
250
|
|
|
252
251
|
The package provides:
|
|
@@ -258,6 +257,10 @@ The package provides:
|
|
|
258
257
|
> [!WARNING]
|
|
259
258
|
> The development branch and associated python package are still experimental, and do not include all features yet. For a stable release, please refer to the main branch.
|
|
260
259
|
|
|
260
|
+
## Documentation
|
|
261
|
+
|
|
262
|
+
You can find the full documentation at [https://champ-hub.github.io/carm-roofline](https://champ-hub.github.io/carm-roofline/).
|
|
263
|
+
|
|
261
264
|
## Installation
|
|
262
265
|
|
|
263
266
|
Install from PyPI:
|
|
@@ -279,10 +282,17 @@ source carm-env/bin/activate
|
|
|
279
282
|
pip install carm-roofline[all]
|
|
280
283
|
```
|
|
281
284
|
|
|
285
|
+
If cloning the repository directly (e.g. for development or access to example applications), use `--recurse-submodules` to also fetch the examples:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
git clone --recurse-submodules https://github.com/champ-hub/carm-roofline
|
|
289
|
+
```
|
|
290
|
+
|
|
282
291
|
## Requirements
|
|
283
292
|
|
|
284
293
|
- Python 3.9+
|
|
285
|
-
-
|
|
294
|
+
- C compiler (e.g. gcc, clang)
|
|
295
|
+
- Optional: [PAPI](https://github.com/icl-utk-edu/papi) headers and binaries for profiling
|
|
286
296
|
|
|
287
297
|
## Command-Line Interface
|
|
288
298
|
|
|
@@ -296,7 +306,7 @@ Available subcommands:
|
|
|
296
306
|
|
|
297
307
|
- `benchmark` - run benchmark suites
|
|
298
308
|
- `gui` - launch the results dashboard
|
|
299
|
-
- `profile` -
|
|
309
|
+
- `profile` - profile an application with PAPI or perf
|
|
300
310
|
|
|
301
311
|
Show help:
|
|
302
312
|
|
|
@@ -304,6 +314,7 @@ Show help:
|
|
|
304
314
|
carm --help
|
|
305
315
|
carm benchmark --help
|
|
306
316
|
carm gui --help
|
|
317
|
+
carm profile --help
|
|
307
318
|
```
|
|
308
319
|
|
|
309
320
|
## Basic Usage
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
# The CARM Tool
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
Cache-Aware Roofline Model (CARM) toolkit - benchmark, visualize and profile Intel, AMD, ARM, RISC-V CPUs and NVIDIA, AMD GPUs.
|
|
6
5
|
|
|
7
6
|
The package provides:
|
|
@@ -13,6 +12,10 @@ The package provides:
|
|
|
13
12
|
> [!WARNING]
|
|
14
13
|
> The development branch and associated python package are still experimental, and do not include all features yet. For a stable release, please refer to the main branch.
|
|
15
14
|
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
You can find the full documentation at [https://champ-hub.github.io/carm-roofline](https://champ-hub.github.io/carm-roofline/).
|
|
18
|
+
|
|
16
19
|
## Installation
|
|
17
20
|
|
|
18
21
|
Install from PyPI:
|
|
@@ -34,10 +37,17 @@ source carm-env/bin/activate
|
|
|
34
37
|
pip install carm-roofline[all]
|
|
35
38
|
```
|
|
36
39
|
|
|
40
|
+
If cloning the repository directly (e.g. for development or access to example applications), use `--recurse-submodules` to also fetch the examples:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone --recurse-submodules https://github.com/champ-hub/carm-roofline
|
|
44
|
+
```
|
|
45
|
+
|
|
37
46
|
## Requirements
|
|
38
47
|
|
|
39
48
|
- Python 3.9+
|
|
40
|
-
-
|
|
49
|
+
- C compiler (e.g. gcc, clang)
|
|
50
|
+
- Optional: [PAPI](https://github.com/icl-utk-edu/papi) headers and binaries for profiling
|
|
41
51
|
|
|
42
52
|
## Command-Line Interface
|
|
43
53
|
|
|
@@ -51,7 +61,7 @@ Available subcommands:
|
|
|
51
61
|
|
|
52
62
|
- `benchmark` - run benchmark suites
|
|
53
63
|
- `gui` - launch the results dashboard
|
|
54
|
-
- `profile` -
|
|
64
|
+
- `profile` - profile an application with PAPI or perf
|
|
55
65
|
|
|
56
66
|
Show help:
|
|
57
67
|
|
|
@@ -59,6 +69,7 @@ Show help:
|
|
|
59
69
|
carm --help
|
|
60
70
|
carm benchmark --help
|
|
61
71
|
carm gui --help
|
|
72
|
+
carm profile --help
|
|
62
73
|
```
|
|
63
74
|
|
|
64
75
|
## Basic Usage
|
|
@@ -5,7 +5,13 @@ import contextvars
|
|
|
5
5
|
from exec_interface import ExecutionInterface
|
|
6
6
|
|
|
7
7
|
from .architecture import Architecture
|
|
8
|
-
from .identity import
|
|
8
|
+
from .identity import (
|
|
9
|
+
MachineSignature,
|
|
10
|
+
detect_machine_signature,
|
|
11
|
+
generate_run_name,
|
|
12
|
+
signature_from_architecture,
|
|
13
|
+
write_machine_json,
|
|
14
|
+
)
|
|
9
15
|
from .memory import SimpleMemoryTopology
|
|
10
16
|
|
|
11
17
|
# Context variable to hold the execution interface, for compatibility with simulation and native execution
|
|
@@ -31,4 +37,5 @@ __all__ = [
|
|
|
31
37
|
"get_execution_interface",
|
|
32
38
|
"set_execution_interface",
|
|
33
39
|
"signature_from_architecture",
|
|
40
|
+
"write_machine_json",
|
|
34
41
|
]
|
|
@@ -11,6 +11,8 @@ machine yields the same name across runs.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
14
16
|
import platform
|
|
15
17
|
import re
|
|
16
18
|
from dataclasses import dataclass
|
|
@@ -22,6 +24,8 @@ from .memory import MemoryLevelInfo, MemoryTopology, MemoryTopologyLike
|
|
|
22
24
|
if TYPE_CHECKING:
|
|
23
25
|
from .architecture import Architecture
|
|
24
26
|
|
|
27
|
+
_BYTES_PER_GIB = 1024**3
|
|
28
|
+
|
|
25
29
|
|
|
26
30
|
@dataclass(frozen=True)
|
|
27
31
|
class CpuInfo:
|
|
@@ -55,17 +59,52 @@ class MachineSignature:
|
|
|
55
59
|
vendor: str
|
|
56
60
|
memory_levels: tuple[MemoryLevelSignature, ...]
|
|
57
61
|
|
|
62
|
+
def _canonical_hash_string(self) -> str:
|
|
63
|
+
"""Canonical string fed to SHA-256 for the config hash.
|
|
64
|
+
|
|
65
|
+
Format: ``arch=<arch>;vendor=<vendor>;levels=<levels>`` where
|
|
66
|
+
``<levels>`` is a ``|``-separated list of
|
|
67
|
+
``<name>:<size_bytes>:<instances>:<num_sharing_threads>``.
|
|
68
|
+
"""
|
|
69
|
+
levels_str = "|".join(
|
|
70
|
+
f"{lvl.name}:{lvl.size_bytes}:{lvl.instances}:{lvl.num_sharing_threads}" for lvl in self.memory_levels
|
|
71
|
+
)
|
|
72
|
+
return f"arch={self.arch};vendor={self.vendor};levels={levels_str}"
|
|
73
|
+
|
|
58
74
|
@property
|
|
59
75
|
def config_hash(self) -> str:
|
|
60
76
|
"""Deterministic 8-char hex SHA256 of non-model-name fields.
|
|
61
77
|
|
|
62
78
|
Hash inputs: arch, vendor, memory_levels (name, size, instances, sharing).
|
|
79
|
+
|
|
80
|
+
.. note::
|
|
81
|
+
DRAM ``size_bytes`` comes from ``/proc/zoneinfo`` ``present`` pages
|
|
82
|
+
(physical capacity, stable across reboots). Falls back to a
|
|
83
|
+
rounded-``MemTotal`` value when zoneinfo is unavailable.
|
|
84
|
+
``num_sharing_threads`` is set to 0 for DRAM (not a hardware
|
|
85
|
+
property; CPU online count may vary). Cache-level fields are
|
|
86
|
+
used verbatim.
|
|
63
87
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
88
|
+
return hashlib.sha256(self._canonical_hash_string().encode("utf-8")).hexdigest()[:8]
|
|
89
|
+
|
|
90
|
+
def to_dict(self) -> dict[str, object]:
|
|
91
|
+
"""Serialize all fields for machine.json debugging output."""
|
|
92
|
+
return {
|
|
93
|
+
"model_name": self.model_name,
|
|
94
|
+
"arch": self.arch,
|
|
95
|
+
"vendor": self.vendor,
|
|
96
|
+
"config_hash": self.config_hash,
|
|
97
|
+
"hash_input": self._canonical_hash_string(),
|
|
98
|
+
"memory_levels": [
|
|
99
|
+
{
|
|
100
|
+
"name": lvl.name,
|
|
101
|
+
"size_bytes": lvl.size_bytes,
|
|
102
|
+
"instances": lvl.instances,
|
|
103
|
+
"num_sharing_threads": lvl.num_sharing_threads,
|
|
104
|
+
}
|
|
105
|
+
for lvl in self.memory_levels
|
|
106
|
+
],
|
|
107
|
+
}
|
|
69
108
|
|
|
70
109
|
|
|
71
110
|
def read_cpuinfo() -> CpuInfo:
|
|
@@ -102,21 +141,69 @@ def read_cpuinfo() -> CpuInfo:
|
|
|
102
141
|
return CpuInfo(model_name=model_name, vendor=vendor)
|
|
103
142
|
|
|
104
143
|
|
|
144
|
+
def _get_physical_ram_bytes() -> int | None:
|
|
145
|
+
"""Total physical RAM (bytes) from ``/proc/zoneinfo``, or ``None``.
|
|
146
|
+
|
|
147
|
+
Sums the ``present`` field across all zones — this is the number of
|
|
148
|
+
physical pages present, derived from the firmware's e820/device-tree
|
|
149
|
+
memory map at boot. It reflects hardware capacity and is stable across
|
|
150
|
+
reboots, unlike ``MemTotal`` which subtracts kernel runtime reservations.
|
|
151
|
+
"""
|
|
152
|
+
try:
|
|
153
|
+
with open("/proc/zoneinfo") as f:
|
|
154
|
+
total_present = 0
|
|
155
|
+
for line in f:
|
|
156
|
+
parts = line.strip().split()
|
|
157
|
+
if len(parts) == 2 and parts[0] == "present":
|
|
158
|
+
total_present += int(parts[1])
|
|
159
|
+
return total_present * os.sysconf("SC_PAGE_SIZE")
|
|
160
|
+
except (OSError, ValueError):
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
|
|
105
164
|
def _levels_from_topology(topology: MemoryTopologyLike | None) -> tuple[MemoryLevelSignature, ...]:
|
|
106
|
-
"""Convert a memory topology to a tuple of deterministic level signatures.
|
|
165
|
+
"""Convert a memory topology to a tuple of deterministic level signatures.
|
|
166
|
+
|
|
167
|
+
DRAM levels use the ``present`` page count from ``/proc/zoneinfo`` (a
|
|
168
|
+
hardware-capacity value stable across reboots). Falls back to rounding
|
|
169
|
+
the topology's ``MemTotal``-based size to the nearest GiB when zoneinfo
|
|
170
|
+
is unavailable (containers, odd kernels).
|
|
171
|
+
|
|
172
|
+
``num_sharing_threads`` is set to 0 for DRAM (not a hardware property;
|
|
173
|
+
``shared_cpu_list`` varies with CPU online count).
|
|
174
|
+
|
|
175
|
+
Cache levels (L1/L2/L3) are passed through unchanged.
|
|
176
|
+
"""
|
|
177
|
+
# Read physical RAM once — stable firmware value
|
|
178
|
+
physical_ram = _get_physical_ram_bytes()
|
|
179
|
+
|
|
107
180
|
if topology is None:
|
|
108
181
|
return ()
|
|
109
182
|
levels: list[MemoryLevelSignature] = []
|
|
110
183
|
for lvl in topology:
|
|
111
184
|
if isinstance(lvl, MemoryLevelInfo):
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
185
|
+
if lvl.name == "DRAM":
|
|
186
|
+
if physical_ram is not None:
|
|
187
|
+
size_bytes = physical_ram
|
|
188
|
+
else:
|
|
189
|
+
size_bytes = round(int(lvl.size) / _BYTES_PER_GIB) * _BYTES_PER_GIB
|
|
190
|
+
levels.append(
|
|
191
|
+
MemoryLevelSignature(
|
|
192
|
+
name=lvl.name,
|
|
193
|
+
size_bytes=size_bytes,
|
|
194
|
+
instances=lvl.instances,
|
|
195
|
+
num_sharing_threads=0,
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
levels.append(
|
|
200
|
+
MemoryLevelSignature(
|
|
201
|
+
name=lvl.name,
|
|
202
|
+
size_bytes=int(lvl.size),
|
|
203
|
+
instances=lvl.instances,
|
|
204
|
+
num_sharing_threads=lvl.num_sharing_threads,
|
|
205
|
+
)
|
|
118
206
|
)
|
|
119
|
-
)
|
|
120
207
|
return tuple(levels)
|
|
121
208
|
|
|
122
209
|
|
|
@@ -206,3 +293,18 @@ def _short_model_name(name: str) -> str:
|
|
|
206
293
|
def generate_run_name(signature: MachineSignature) -> str:
|
|
207
294
|
"""Generate ``"<short_model>_<config_hash>"`` (e.g. ``"Ryzen-7-7735HS_59486dd1"``)."""
|
|
208
295
|
return f"{_short_model_name(signature.model_name)}_{signature.config_hash}"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def write_machine_json(signature: MachineSignature, directory: Path) -> None:
|
|
299
|
+
"""Write machine.json to *directory* if it does not already exist.
|
|
300
|
+
|
|
301
|
+
The file contains the full MachineSignature serialized as JSON, including
|
|
302
|
+
all hash inputs and the computed hash, so future runs can debug why a
|
|
303
|
+
machine hash changed.
|
|
304
|
+
"""
|
|
305
|
+
path = directory / "machine.json"
|
|
306
|
+
if path.exists():
|
|
307
|
+
return
|
|
308
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
309
|
+
with open(path, "x", encoding="utf-8") as f:
|
|
310
|
+
json.dump(signature.to_dict(), f, indent=2, sort_keys=True)
|
|
@@ -33,7 +33,7 @@ def _collect_gops_by_isa(isa_suites: dict[str, ISABenchmarkSuite]) -> dict[str,
|
|
|
33
33
|
if not isinstance(bench.results, ArithmeticBenchmarkResult):
|
|
34
34
|
continue
|
|
35
35
|
|
|
36
|
-
gops_value = float(bench.results.performance
|
|
36
|
+
gops_value = float(bench.results.performance) / 1e9
|
|
37
37
|
if not math.isfinite(gops_value):
|
|
38
38
|
warn(f"Skipping invalid GOPS value for ISA {isa}: {gops_value}")
|
|
39
39
|
continue
|
|
@@ -73,7 +73,7 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
|
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
ops_per_inst = isa_instance.ops_per_inst(benchmark.params.data_type, benchmark.params.operation)
|
|
76
|
-
total_insts = (benchmark.params.num_ops
|
|
76
|
+
total_insts = (int(benchmark.params.num_ops) // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
|
|
77
77
|
cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
|
|
78
78
|
ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
|
|
79
79
|
ops_per_cycle = ops_per_inst * ipc
|
|
@@ -84,17 +84,17 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
|
|
|
84
84
|
"operation": benchmark.params.operation.name,
|
|
85
85
|
"benchmark": benchmark_name,
|
|
86
86
|
"threads": benchmark.params.num_threads,
|
|
87
|
-
"gops": float(res.performance
|
|
87
|
+
"gops": float(res.performance) / 1e9,
|
|
88
88
|
"gops_display": str(res.performance),
|
|
89
89
|
"ipc": ipc,
|
|
90
|
-
"frequency_hz": float(frequency
|
|
90
|
+
"frequency_hz": float(frequency),
|
|
91
91
|
"frequency_display": str(frequency),
|
|
92
92
|
"ops_per_instruction": ops_per_inst,
|
|
93
93
|
"ops_per_cycle": ops_per_cycle,
|
|
94
|
-
"time_seconds": float(res.time_taken
|
|
94
|
+
"time_seconds": float(res.time_taken),
|
|
95
95
|
"time_display": str(res.time_taken),
|
|
96
96
|
"repetitions": int(res.num_repetitions),
|
|
97
|
-
"cycles": float(cycles
|
|
97
|
+
"cycles": float(cycles),
|
|
98
98
|
"cycles_display": str(cycles),
|
|
99
99
|
}
|
|
100
100
|
)
|
|
@@ -40,7 +40,7 @@ def _serialize_arithmetic(
|
|
|
40
40
|
assert isinstance(res, ArithmeticBenchmarkResult)
|
|
41
41
|
|
|
42
42
|
ops_per_inst = isa_instance.ops_per_inst(bench.params.data_type, bench.params.operation)
|
|
43
|
-
total_insts = (bench.params.num_ops
|
|
43
|
+
total_insts = (int(bench.params.num_ops) // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
|
|
44
44
|
cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
|
|
45
45
|
ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
|
|
46
46
|
|
|
@@ -54,15 +54,15 @@ def _serialize_arithmetic(
|
|
|
54
54
|
"timestamp": timestamp,
|
|
55
55
|
"machine": machine,
|
|
56
56
|
"operation": bench.params.operation.name,
|
|
57
|
-
"num_ops": int(bench.params.num_ops
|
|
58
|
-
"performance_gops": res.performance
|
|
57
|
+
"num_ops": int(bench.params.num_ops),
|
|
58
|
+
"performance_gops": float(res.performance) / 1e9,
|
|
59
59
|
"ipc": ipc,
|
|
60
|
-
"frequency_hz": frequency
|
|
60
|
+
"frequency_hz": float(frequency),
|
|
61
61
|
"ops_per_instruction": ops_per_inst,
|
|
62
62
|
"ops_per_cycle": ops_per_inst * ipc,
|
|
63
|
-
"time_seconds": res.time_taken
|
|
63
|
+
"time_seconds": float(res.time_taken),
|
|
64
64
|
"repetitions": res.num_repetitions,
|
|
65
|
-
"cycles": cycles
|
|
65
|
+
"cycles": int(cycles),
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
|
|
@@ -81,7 +81,7 @@ def _serialize_memory(
|
|
|
81
81
|
assert isinstance(res, MemoryBenchmarkResult)
|
|
82
82
|
|
|
83
83
|
bpi = isa_instance.bytes_per_inst(bench.params.data_type)
|
|
84
|
-
total_insts = (bench.working_set_bytes
|
|
84
|
+
total_insts = (int(bench.working_set_bytes) // bpi) * res.num_repetitions
|
|
85
85
|
cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
|
|
86
86
|
ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
|
|
87
87
|
|
|
@@ -99,14 +99,14 @@ def _serialize_memory(
|
|
|
99
99
|
"num_stores": bench.params.num_st,
|
|
100
100
|
"cache_level": bench.cache_level,
|
|
101
101
|
"memory_level_name": bench.params.memory_level_name,
|
|
102
|
-
"size_per_thread_bytes": bench.params.size_per_thread
|
|
103
|
-
"working_set_bytes": bench.working_set_bytes
|
|
102
|
+
"size_per_thread_bytes": int(bench.params.size_per_thread),
|
|
103
|
+
"working_set_bytes": int(bench.working_set_bytes),
|
|
104
104
|
"layout_mode": bench.params.layout_mode.value,
|
|
105
|
-
"bandwidth_gbps": res.bandwidth
|
|
105
|
+
"bandwidth_gbps": float(res.bandwidth) / 1e9,
|
|
106
106
|
"ipc": ipc,
|
|
107
|
-
"time_seconds": res.time_taken
|
|
107
|
+
"time_seconds": float(res.time_taken),
|
|
108
108
|
"repetitions": res.num_repetitions,
|
|
109
|
-
"cycles": cycles
|
|
109
|
+
"cycles": int(cycles),
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
|
|
@@ -40,7 +40,7 @@ def _collect_bandwidth_by_label(isa_suites: dict[str, ISABenchmarkSuite]) -> dic
|
|
|
40
40
|
continue
|
|
41
41
|
|
|
42
42
|
level = res.cache_level or "unknown"
|
|
43
|
-
bandwidth_value = float(res.bandwidth
|
|
43
|
+
bandwidth_value = float(res.bandwidth) / 1e9
|
|
44
44
|
if not math.isfinite(bandwidth_value):
|
|
45
45
|
warn(f"Skipping invalid bandwidth value for {isa}/{name}: {bandwidth_value}")
|
|
46
46
|
continue
|
|
@@ -84,7 +84,7 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
|
|
|
84
84
|
assert bench.results is not None # for type checker
|
|
85
85
|
level = bench.results.cache_level or "unknown"
|
|
86
86
|
bytes_per_inst = isa_instance.bytes_per_inst(bench.params.data_type)
|
|
87
|
-
total_insts = (bench.working_set_bytes
|
|
87
|
+
total_insts = (int(bench.working_set_bytes) // bytes_per_inst) * bench.results.num_repetitions
|
|
88
88
|
cycles = Cycles.from_time_and_frequency(bench.results.time_taken, frequency)
|
|
89
89
|
|
|
90
90
|
ipc = total_insts / cycles.value
|
|
@@ -79,7 +79,7 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
|
|
|
79
79
|
for _name, bench in sorted(mem_benchmarks.items(), key=lambda kv: kv[0]):
|
|
80
80
|
assert bench.results is not None # for type checker
|
|
81
81
|
bytes_per_inst = isa_instance.bytes_per_inst(bench.params.data_type)
|
|
82
|
-
total_insts = (bench.working_set_bytes
|
|
82
|
+
total_insts = (int(bench.working_set_bytes) // bytes_per_inst) * bench.results.num_repetitions
|
|
83
83
|
cycles = Cycles.from_time_and_frequency(bench.results.time_taken, frequency)
|
|
84
84
|
cache_level = bench.results.cache_level or "unknown"
|
|
85
85
|
table.add_row(
|
|
@@ -114,22 +114,22 @@ def _collect_sweep_series(
|
|
|
114
114
|
for ws_bytes, cache_level, bandwidth in suite.get_sweep_data():
|
|
115
115
|
if bandwidth is None:
|
|
116
116
|
continue
|
|
117
|
-
bw_val = float(bandwidth
|
|
117
|
+
bw_val = float(bandwidth) / 1e9
|
|
118
118
|
if not math.isfinite(bw_val):
|
|
119
119
|
continue
|
|
120
|
-
points.append((ws_bytes
|
|
120
|
+
points.append((int(ws_bytes), cache_level, bw_val))
|
|
121
121
|
else:
|
|
122
122
|
mem_benchmarks = suite.get_memory_benchmarks()
|
|
123
123
|
for _name, bench in sorted(mem_benchmarks.items(), key=lambda kv: kv[0]):
|
|
124
124
|
res = bench.results
|
|
125
125
|
if not isinstance(res, MemoryBenchmarkResult):
|
|
126
126
|
continue
|
|
127
|
-
bw_val = float(res.bandwidth
|
|
127
|
+
bw_val = float(res.bandwidth) / 1e9
|
|
128
128
|
if not math.isfinite(bw_val):
|
|
129
129
|
continue
|
|
130
130
|
ws = bench.working_set_bytes
|
|
131
131
|
cache_level = res.cache_level or "unknown"
|
|
132
|
-
points.append((ws
|
|
132
|
+
points.append((int(ws), cache_level, bw_val))
|
|
133
133
|
|
|
134
134
|
if points:
|
|
135
135
|
points.sort(key=lambda t: t[0])
|
|
@@ -114,7 +114,7 @@ def _cache_sizes_kib(context: CARMContext) -> tuple[int, int, int]:
|
|
|
114
114
|
size = getattr(level, "size", None)
|
|
115
115
|
if size is None:
|
|
116
116
|
continue
|
|
117
|
-
kib = int(size
|
|
117
|
+
kib = int(size) // 1024
|
|
118
118
|
if name == "L1":
|
|
119
119
|
l1_kib = kib
|
|
120
120
|
elif name == "L2":
|
|
@@ -131,7 +131,7 @@ def _cache_sizes_kib(context: CARMContext) -> tuple[int, int, int]:
|
|
|
131
131
|
def _find_dram_working_set_bytes(suite: ISABenchmarkSuite) -> int:
|
|
132
132
|
for bench in suite.get_memory_benchmarks().values():
|
|
133
133
|
if bench.cache_level == "DRAM" and getattr(bench, "working_set_bytes", None) is not None:
|
|
134
|
-
return int(bench.working_set_bytes
|
|
134
|
+
return int(bench.working_set_bytes)
|
|
135
135
|
return 0
|
|
136
136
|
|
|
137
137
|
|
|
@@ -175,8 +175,8 @@ def _aggregate_level_metrics(
|
|
|
175
175
|
level = bench.cache_level
|
|
176
176
|
assert level is not None
|
|
177
177
|
bytes_per_inst = isa_instance.bytes_per_inst(bench.params.data_type)
|
|
178
|
-
bandwidth_by_level[level] = float(bench.results.bandwidth
|
|
179
|
-
total_insts = (bench.working_set_bytes
|
|
178
|
+
bandwidth_by_level[level] = float(bench.results.bandwidth) / 1e9
|
|
179
|
+
total_insts = (int(bench.working_set_bytes) // bytes_per_inst) * bench.results.num_repetitions
|
|
180
180
|
cycles = Cycles.from_time_and_frequency(bench.results.time_taken, frequency)
|
|
181
181
|
ipc_by_level[level] = 0.0 if cycles.value == 0 else total_insts / cycles.value
|
|
182
182
|
|
|
@@ -208,12 +208,10 @@ def _pick_fp_for_combo(
|
|
|
208
208
|
if bench.params.data_type != dt or bench.params.num_threads != nt:
|
|
209
209
|
continue
|
|
210
210
|
ops_per_inst = isa_instance.ops_per_inst(bench.params.data_type, bench.params.operation)
|
|
211
|
-
total_insts = (
|
|
212
|
-
(bench.params.num_ops.value // ops_per_inst) * bench.results.num_repetitions if ops_per_inst else 0
|
|
213
|
-
)
|
|
211
|
+
total_insts = (int(bench.params.num_ops) // ops_per_inst) * bench.results.num_repetitions if ops_per_inst else 0
|
|
214
212
|
cycles = Cycles.from_time_and_frequency(bench.results.time_taken, frequency)
|
|
215
213
|
ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
|
|
216
|
-
gflops = float(bench.results.performance
|
|
214
|
+
gflops = float(bench.results.performance) / 1e9
|
|
217
215
|
|
|
218
216
|
if bench.params.operation == ArithmeticOperation.fma:
|
|
219
217
|
fma_gflops = gflops
|
|
@@ -94,14 +94,14 @@ class MemorySweepBenchmarkSuite(ISABenchmarkSuite):
|
|
|
94
94
|
# Step 2 - Determine sweep range.
|
|
95
95
|
# ------------------------------------------------------------------
|
|
96
96
|
_, _, l1_avail, _ = level_table[0]
|
|
97
|
-
min_size_bytes: float = 0.1 * float(l1_avail
|
|
97
|
+
min_size_bytes: float = 0.1 * float(l1_avail)
|
|
98
98
|
|
|
99
99
|
if len(level_table) >= 2:
|
|
100
100
|
_, _, last_cache_avail, _ = level_table[-2]
|
|
101
101
|
else:
|
|
102
102
|
_, _, last_cache_avail, _ = level_table[-1]
|
|
103
103
|
|
|
104
|
-
max_size_bytes: float = 32.0 * float(last_cache_avail
|
|
104
|
+
max_size_bytes: float = 32.0 * float(last_cache_avail)
|
|
105
105
|
|
|
106
106
|
if max_size_bytes <= min_size_bytes:
|
|
107
107
|
raise ValueError(
|
|
@@ -200,5 +200,5 @@ class MemorySweepBenchmarkSuite(ISABenchmarkSuite):
|
|
|
200
200
|
level = bench.cache_level if bench.cache_level is not None else "UNKNOWN"
|
|
201
201
|
rows.append((bench.working_set_bytes, level, bw))
|
|
202
202
|
|
|
203
|
-
rows.sort(key=lambda t: t[0]
|
|
203
|
+
rows.sort(key=lambda t: t[0])
|
|
204
204
|
return rows
|
|
@@ -17,6 +17,7 @@ from architecture import (
|
|
|
17
17
|
generate_run_name,
|
|
18
18
|
set_execution_interface,
|
|
19
19
|
signature_from_architecture,
|
|
20
|
+
write_machine_json,
|
|
20
21
|
)
|
|
21
22
|
from arguments import InsertsArguments, TopLevelHelpFormatter
|
|
22
23
|
from benchmark.interface import run_full_benchmark
|
|
@@ -58,8 +59,9 @@ def _handle_benchmark(args: argparse.Namespace) -> int:
|
|
|
58
59
|
# Make exec_iface globally available for architecture feature detection
|
|
59
60
|
set_execution_interface(exec_iface)
|
|
60
61
|
architecture = Architecture(args)
|
|
62
|
+
signature = signature_from_architecture(architecture)
|
|
61
63
|
if args.name is None:
|
|
62
|
-
args.name = generate_run_name(
|
|
64
|
+
args.name = generate_run_name(signature)
|
|
63
65
|
benchmarking = Benchmarking(args)
|
|
64
66
|
run_config = RunConfig(args)
|
|
65
67
|
|
|
@@ -73,6 +75,7 @@ def _handle_benchmark(args: argparse.Namespace) -> int:
|
|
|
73
75
|
detail("Dry run finished: benchmark code generated successfully.")
|
|
74
76
|
return 0
|
|
75
77
|
|
|
78
|
+
write_machine_json(signature, run_config.output_dir / run_config.name)
|
|
76
79
|
output_benchmark_results(context, benchmark_suites)
|
|
77
80
|
return 0
|
|
78
81
|
|
|
@@ -126,7 +129,7 @@ SUBPARSERS = (
|
|
|
126
129
|
),
|
|
127
130
|
SubParserInfo(
|
|
128
131
|
name="profile",
|
|
129
|
-
help_short="Profile applications
|
|
132
|
+
help_short="Profile applications for CARM analysis",
|
|
130
133
|
help_long="Profile instrumented applications to compute roofline metrics (AI, GFLOP/s, bandwidth). "
|
|
131
134
|
"Supports MPI, threaded, and hybrid applications. "
|
|
132
135
|
"Usage: carm profile [options] -- <command>",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-roofline
|
|
3
|
-
Version: 1.0.2.
|
|
3
|
+
Version: 1.0.2.dev2
|
|
4
4
|
Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
|
|
5
5
|
Author: CHaMP Team
|
|
6
6
|
License: Apache License
|
|
@@ -246,7 +246,6 @@ Dynamic: license-file
|
|
|
246
246
|
|
|
247
247
|
# The CARM Tool
|
|
248
248
|
|
|
249
|
-
|
|
250
249
|
Cache-Aware Roofline Model (CARM) toolkit - benchmark, visualize and profile Intel, AMD, ARM, RISC-V CPUs and NVIDIA, AMD GPUs.
|
|
251
250
|
|
|
252
251
|
The package provides:
|
|
@@ -258,6 +257,10 @@ The package provides:
|
|
|
258
257
|
> [!WARNING]
|
|
259
258
|
> The development branch and associated python package are still experimental, and do not include all features yet. For a stable release, please refer to the main branch.
|
|
260
259
|
|
|
260
|
+
## Documentation
|
|
261
|
+
|
|
262
|
+
You can find the full documentation at [https://champ-hub.github.io/carm-roofline](https://champ-hub.github.io/carm-roofline/).
|
|
263
|
+
|
|
261
264
|
## Installation
|
|
262
265
|
|
|
263
266
|
Install from PyPI:
|
|
@@ -279,10 +282,17 @@ source carm-env/bin/activate
|
|
|
279
282
|
pip install carm-roofline[all]
|
|
280
283
|
```
|
|
281
284
|
|
|
285
|
+
If cloning the repository directly (e.g. for development or access to example applications), use `--recurse-submodules` to also fetch the examples:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
git clone --recurse-submodules https://github.com/champ-hub/carm-roofline
|
|
289
|
+
```
|
|
290
|
+
|
|
282
291
|
## Requirements
|
|
283
292
|
|
|
284
293
|
- Python 3.9+
|
|
285
|
-
-
|
|
294
|
+
- C compiler (e.g. gcc, clang)
|
|
295
|
+
- Optional: [PAPI](https://github.com/icl-utk-edu/papi) headers and binaries for profiling
|
|
286
296
|
|
|
287
297
|
## Command-Line Interface
|
|
288
298
|
|
|
@@ -296,7 +306,7 @@ Available subcommands:
|
|
|
296
306
|
|
|
297
307
|
- `benchmark` - run benchmark suites
|
|
298
308
|
- `gui` - launch the results dashboard
|
|
299
|
-
- `profile` -
|
|
309
|
+
- `profile` - profile an application with PAPI or perf
|
|
300
310
|
|
|
301
311
|
Show help:
|
|
302
312
|
|
|
@@ -304,6 +314,7 @@ Show help:
|
|
|
304
314
|
carm --help
|
|
305
315
|
carm benchmark --help
|
|
306
316
|
carm gui --help
|
|
317
|
+
carm profile --help
|
|
307
318
|
```
|
|
308
319
|
|
|
309
320
|
## Basic Usage
|