carm-roofline 1.0.0.dev5__tar.gz → 1.0.1.dev0__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.0.dev5 → carm_roofline-1.0.1.dev0}/PKG-INFO +3 -2
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/architecture.py +6 -7
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/config.py +8 -8
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/detect.py +3 -3
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/memory.py +7 -7
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/benchmarking.py +17 -19
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/x86.py +1 -1
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/interface.py +6 -2
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/memory.py +16 -2
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/roofline.py +1 -1
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm.py +45 -39
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/PKG-INFO +3 -2
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/SOURCES.txt +9 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/requires.txt +1 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/top_level.txt +2 -0
- carm_roofline-1.0.1.dev0/error.py +5 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/exec_interface.py +0 -2
- carm_roofline-1.0.1.dev0/profiling/__init__.py +151 -0
- carm_roofline-1.0.1.dev0/profiling/aggregation.py +215 -0
- carm_roofline-1.0.1.dev0/profiling/backends.py +179 -0
- carm_roofline-1.0.1.dev0/profiling/config.py +114 -0
- carm_roofline-1.0.1.dev0/profiling/loaders.py +172 -0
- carm_roofline-1.0.1.dev0/profiling/metrics.py +429 -0
- carm_roofline-1.0.1.dev0/profiling/model.py +143 -0
- carm_roofline-1.0.1.dev0/profiling/output.py +131 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/pyproject.toml +7 -4
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/run_config.py +9 -1
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test/test_memory_suite_generation.py +1 -0
- carm_roofline-1.0.1.dev0/workspace.py +44 -0
- carm_roofline-1.0.0.dev5/error.py +0 -5
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/LICENSE +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/README.md +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/arm.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/frequency.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/riscv.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/arm/features.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/arm/frequency.h +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/arm/vlen.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/frequency.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/riscv/frequency.h +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/riscv/rvv_version.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/riscv/vlen.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/x86/cache.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/x86/features.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/x86/frequency.h +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/tests/x86/x86_avx512/frequency.h +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/architecture/x86.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/arguments.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/benchmark.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/arm.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/code_gen/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/code_gen/data_type.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/code_gen/instruction.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/code_gen/operation.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/code_gen/register.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/isa.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/parameters.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/generation/riscv.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/arithmetic.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/base.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/common.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/memory.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/memory_sweep.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/mixed.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/output/roofline.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/result.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/arithmetic.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/base.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/memory_sweep.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/benchmark/suites/mixed.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/dependency_links.txt +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/carm_roofline.egg-info/entry_points.txt +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/context.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/assets/CARM_icon.svg +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/assets/CHAMP_logo.svg +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/assets/menu_icon.png +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/assets/style.css +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/config.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/dashboard.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/gui/gui_utils.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/output_utils.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/results_paths.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/setup.cfg +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test/test_isa_codegen_integration.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test/test_output_module.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test/test_typed_benchmarks.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test_bench/__init__.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test_bench/builder.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test_bench/test_bench.c +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test_bench/test_bench.h +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/test_bench/wrapper.inl +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/units.py +0 -0
- {carm_roofline-1.0.0.dev5 → carm_roofline-1.0.1.dev0}/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-roofline
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1.dev0
|
|
4
4
|
Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
|
|
5
5
|
Author: CHaMP Team
|
|
6
6
|
License: GNU LESSER GENERAL PUBLIC LICENSE
|
|
@@ -518,7 +518,7 @@ Classifier: Intended Audience :: Science/Research
|
|
|
518
518
|
Classifier: Topic :: Software Development :: Testing
|
|
519
519
|
Classifier: Topic :: System :: Benchmark
|
|
520
520
|
Classifier: Programming Language :: Python :: 3
|
|
521
|
-
Classifier: Programming Language :: Python :: 3.
|
|
521
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
522
522
|
Classifier: Operating System :: POSIX :: Linux
|
|
523
523
|
Requires-Python: >=3.9
|
|
524
524
|
Description-Content-Type: text/markdown
|
|
@@ -529,6 +529,7 @@ Requires-Dist: numpy>=2.0.0
|
|
|
529
529
|
Requires-Dist: matplotlib>=3.7.2
|
|
530
530
|
Requires-Dist: tomli>=2.0.0
|
|
531
531
|
Requires-Dist: platformdirs>=4.0.0
|
|
532
|
+
Requires-Dist: argcomplete>=3.0.0
|
|
532
533
|
Provides-Extra: gui
|
|
533
534
|
Requires-Dist: dash[diskcache]; extra == "gui"
|
|
534
535
|
Requires-Dist: dash-bootstrap-components; extra == "gui"
|
|
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
6
6
|
|
|
7
7
|
from arguments import InsertsArguments, positive_int
|
|
8
8
|
from benchmark import generation as bench_gen
|
|
9
|
-
from error import
|
|
9
|
+
from error import UserError
|
|
10
10
|
from output_utils import configure_verbosity, debug, detail, format_if_container, warn
|
|
11
11
|
from units import Frequency
|
|
12
12
|
|
|
@@ -28,14 +28,14 @@ def check_isa_compatibility(selected_isas: list[type[BaseISA]]) -> None:
|
|
|
28
28
|
families.add(family)
|
|
29
29
|
|
|
30
30
|
if len(families) > 1:
|
|
31
|
-
raise
|
|
31
|
+
raise UserError(f"Incompatible ISA families selected: {', '.join(families)}")
|
|
32
32
|
|
|
33
33
|
# Check for special incompatibilities within the same family
|
|
34
34
|
isa_set = set(selected_isas)
|
|
35
35
|
for incompatible_pair in bench_gen.INCOMPATIBLE_ISAS:
|
|
36
36
|
if incompatible_pair.issubset(isa_set):
|
|
37
37
|
pair_names = ", ".join(isa.name for isa in incompatible_pair)
|
|
38
|
-
raise
|
|
38
|
+
raise UserError(f"Incompatible ISAs selected: {pair_names}")
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def positive_po2_int(arg: str) -> int:
|
|
@@ -278,20 +278,18 @@ class Architecture(InsertsArguments):
|
|
|
278
278
|
parser.add_argument(
|
|
279
279
|
"-i",
|
|
280
280
|
"--isa",
|
|
281
|
-
nargs="
|
|
281
|
+
nargs="+",
|
|
282
282
|
choices=list(ISA_NAME_TO_CLASS.keys()),
|
|
283
283
|
help="Set of ISAs to test. (Default: automatically detects all available ISAs)",
|
|
284
284
|
)
|
|
285
285
|
parser.add_argument(
|
|
286
286
|
"-l",
|
|
287
287
|
"--vector-length",
|
|
288
|
-
nargs="?",
|
|
289
288
|
type=positive_po2_int,
|
|
290
289
|
help="Vector register length in bytes. (Default: automatically detected)",
|
|
291
290
|
)
|
|
292
291
|
parser.add_argument(
|
|
293
292
|
"--vector-lmul",
|
|
294
|
-
nargs="?",
|
|
295
293
|
type=int,
|
|
296
294
|
choices=(1, 2, 4, 8),
|
|
297
295
|
help="Vector register group modifier (LMUL). RISC-V specific.",
|
|
@@ -309,12 +307,13 @@ class Architecture(InsertsArguments):
|
|
|
309
307
|
"--emit-config",
|
|
310
308
|
type=str,
|
|
311
309
|
metavar="PATH",
|
|
310
|
+
nargs="?",
|
|
311
|
+
const="topology.toml",
|
|
312
312
|
help="Generate a template TOML configuration file at the specified path and exit. "
|
|
313
313
|
"Edit the template and pass it back with --topology-config.",
|
|
314
314
|
)
|
|
315
315
|
parser.add_argument(
|
|
316
316
|
"--frequency",
|
|
317
|
-
nargs="?",
|
|
318
317
|
type=Frequency.from_argparse,
|
|
319
318
|
help="Processor frequency, e.g. '2GHz', '3200mhz' (Default: automatically detected if possible)",
|
|
320
319
|
)
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import cast
|
|
20
20
|
|
|
21
|
-
from error import
|
|
21
|
+
from error import UserError
|
|
22
22
|
from units import Bytes
|
|
23
23
|
|
|
24
24
|
from .memory import SimpleMemoryTopology
|
|
@@ -81,13 +81,13 @@ def load_memory_topology_from_toml(config_path: Path | str) -> SimpleMemoryTopol
|
|
|
81
81
|
"""
|
|
82
82
|
config_path = Path(config_path)
|
|
83
83
|
if not config_path.exists():
|
|
84
|
-
raise
|
|
84
|
+
raise UserError(f"Config file not found: {config_path}")
|
|
85
85
|
|
|
86
86
|
try:
|
|
87
87
|
with open(config_path, "rb") as f:
|
|
88
88
|
config = tomllib.load(f)
|
|
89
89
|
except Exception as e:
|
|
90
|
-
raise
|
|
90
|
+
raise UserError(f"Failed to parse TOML file '{config_path}': {e}") from e
|
|
91
91
|
|
|
92
92
|
total_cpus = _read_optional_int(config, "total_cpus")
|
|
93
93
|
smt_degree = cast(int, _read_optional_int(config, "smt_degree", 1))
|
|
@@ -96,7 +96,7 @@ def load_memory_topology_from_toml(config_path: Path | str) -> SimpleMemoryTopol
|
|
|
96
96
|
# Extract cache levels
|
|
97
97
|
cache_levels_data = config.get("cache_levels", [])
|
|
98
98
|
if not isinstance(cache_levels_data, list) or not cache_levels_data:
|
|
99
|
-
raise
|
|
99
|
+
raise UserError("Configuration must contain at least one [[cache_levels]] section")
|
|
100
100
|
|
|
101
101
|
# Parse and validate each cache level (processed in document order; order determines level numbers)
|
|
102
102
|
level_sizes: list[Bytes] = []
|
|
@@ -104,17 +104,17 @@ def load_memory_topology_from_toml(config_path: Path | str) -> SimpleMemoryTopol
|
|
|
104
104
|
|
|
105
105
|
for level_dict in cache_levels_data:
|
|
106
106
|
if not isinstance(level_dict, dict):
|
|
107
|
-
raise
|
|
107
|
+
raise UserError(f"Each [[cache_levels]] entry must be a table, got: {type(level_dict).__name__}")
|
|
108
108
|
try:
|
|
109
109
|
instances = level_dict["instances"]
|
|
110
110
|
size_str = level_dict["size"]
|
|
111
111
|
except KeyError as e:
|
|
112
|
-
raise
|
|
112
|
+
raise UserError(f"Cache level section missing required field: {e}") from e
|
|
113
113
|
|
|
114
114
|
if not isinstance(instances, int):
|
|
115
|
-
raise
|
|
115
|
+
raise UserError(f"'instances' must be an integer, got: {instances!r}")
|
|
116
116
|
if not isinstance(size_str, str):
|
|
117
|
-
raise
|
|
117
|
+
raise UserError(f"'size' must be a string, got: {size_str!r}")
|
|
118
118
|
|
|
119
119
|
size = Bytes.from_string(size_str)
|
|
120
120
|
level_sizes.append(size)
|
|
@@ -15,7 +15,7 @@ from dataclasses import dataclass
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from typing import TYPE_CHECKING, Any
|
|
17
17
|
|
|
18
|
-
from error import
|
|
18
|
+
from error import UserError
|
|
19
19
|
from output_utils import debug, detail, warn
|
|
20
20
|
from units import Frequency
|
|
21
21
|
|
|
@@ -244,7 +244,7 @@ def detect_cache(ctx: TestContext) -> dict[str, Any]:
|
|
|
244
244
|
else:
|
|
245
245
|
raise ValueError("MemoryTopology parsed but contains no CPU data")
|
|
246
246
|
except Exception as e:
|
|
247
|
-
raise
|
|
247
|
+
raise UserError(
|
|
248
248
|
f"Failed to parse native system memory topology from /sys: {e}. "
|
|
249
249
|
"For non-native execution or when sysfs is unavailable, "
|
|
250
250
|
"please provide cache hierarchy explicitly with --topology-config."
|
|
@@ -424,7 +424,7 @@ def native_detect(threads: int = 1) -> DetectedArchitecture:
|
|
|
424
424
|
"""
|
|
425
425
|
machine = platform.machine().lower()
|
|
426
426
|
if machine == "":
|
|
427
|
-
raise
|
|
427
|
+
raise UserError(
|
|
428
428
|
"Unable to detect machine architecture (platform.machine() returned empty string). Please specify the "
|
|
429
429
|
"architecture manually (--isa <arch>)."
|
|
430
430
|
)
|
|
@@ -9,7 +9,7 @@ from enum import Enum
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Protocol, runtime_checkable
|
|
11
11
|
|
|
12
|
-
from error import
|
|
12
|
+
from error import UserError
|
|
13
13
|
from output_utils import debug, format_if_container
|
|
14
14
|
from units import Bytes
|
|
15
15
|
|
|
@@ -168,26 +168,26 @@ class SimpleMemoryTopology:
|
|
|
168
168
|
"""
|
|
169
169
|
|
|
170
170
|
if len(level_sizes) != len(instances_per_level):
|
|
171
|
-
raise
|
|
171
|
+
raise UserError(
|
|
172
172
|
"Level sizes and instances_per_level must have same length: "
|
|
173
173
|
f"{len(level_sizes)} vs {len(instances_per_level)}"
|
|
174
174
|
)
|
|
175
175
|
if not level_sizes:
|
|
176
|
-
raise
|
|
176
|
+
raise UserError("At least one cache level must be provided")
|
|
177
177
|
|
|
178
178
|
if any(inst <= 0 for inst in instances_per_level):
|
|
179
|
-
raise
|
|
179
|
+
raise UserError("All values in instances_per_level must be positive integers")
|
|
180
180
|
|
|
181
181
|
if total_cpus is not None and total_cpus <= 0:
|
|
182
|
-
raise
|
|
182
|
+
raise UserError(f"total_cpus must be positive when provided, got {total_cpus}")
|
|
183
183
|
|
|
184
184
|
if cpu_offset < 0:
|
|
185
|
-
raise
|
|
185
|
+
raise UserError(f"cpu_offset must be >= 0, got {cpu_offset}")
|
|
186
186
|
|
|
187
187
|
if total_cpus is not None:
|
|
188
188
|
for idx, instances in enumerate(instances_per_level, 1):
|
|
189
189
|
if instances > total_cpus:
|
|
190
|
-
raise
|
|
190
|
+
raise UserError(
|
|
191
191
|
f"Cache level {idx}: instances ({instances}) cannot exceed total_cpus ({total_cpus})"
|
|
192
192
|
)
|
|
193
193
|
|
|
@@ -11,7 +11,7 @@ from enum import Enum
|
|
|
11
11
|
from arguments import InsertsArguments, enum_action, positive_float, positive_int
|
|
12
12
|
from benchmark.generation.code_gen.operation import ArithmeticOperation
|
|
13
13
|
from output_utils import warn
|
|
14
|
-
from units import Operations
|
|
14
|
+
from units import Bytes, Operations
|
|
15
15
|
|
|
16
16
|
from .generation import DataType
|
|
17
17
|
|
|
@@ -66,27 +66,29 @@ def ld_st_ratio_type(arg: str) -> LoadStoreRatio:
|
|
|
66
66
|
return LoadStoreRatio(ld, st)
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def mem_test_size_type(arg: str) ->
|
|
69
|
+
def mem_test_size_type(arg: str) -> Bytes | None:
|
|
70
70
|
"""Parse a memory test size specifier.
|
|
71
71
|
|
|
72
72
|
Accepts either:
|
|
73
|
-
- A
|
|
73
|
+
- A quantity with a binary unit (e.g. "16 KiB", "2 MiB", "8 GiB")
|
|
74
74
|
- The string "auto" (case-insensitive) for automatic sizing
|
|
75
75
|
|
|
76
76
|
Returns:
|
|
77
|
-
|
|
77
|
+
Bytes or None (for "auto")
|
|
78
78
|
|
|
79
79
|
Raises:
|
|
80
80
|
argparse.ArgumentTypeError on invalid input.
|
|
81
81
|
"""
|
|
82
82
|
s = str(arg).strip().lower()
|
|
83
83
|
if s == "auto":
|
|
84
|
-
return
|
|
84
|
+
return None
|
|
85
85
|
try:
|
|
86
|
-
return
|
|
87
|
-
except
|
|
86
|
+
return Bytes.from_argparse(arg)
|
|
87
|
+
except argparse.ArgumentTypeError:
|
|
88
|
+
raise
|
|
89
|
+
except Exception as e:
|
|
88
90
|
raise argparse.ArgumentTypeError(
|
|
89
|
-
f"invalid memory test size: {arg!r}.
|
|
91
|
+
f"invalid memory test size: {arg!r}. Expected a binary unit string like '16 KiB', '2 MiB', or 'auto'. ({e})"
|
|
90
92
|
) from e
|
|
91
93
|
|
|
92
94
|
|
|
@@ -106,7 +108,7 @@ class Benchmarking(InsertsArguments):
|
|
|
106
108
|
self.num_ops: Operations = Operations(args.num_ops)
|
|
107
109
|
self.ld_st_ratio: LoadStoreRatio = args.ld_st_ratio
|
|
108
110
|
self.arith_mem_ratio: int = args.arith_mem_ratio
|
|
109
|
-
self.mem_test_sizes: list[
|
|
111
|
+
self.mem_test_sizes: list[Bytes | None] | None = args.mem_test_sizes
|
|
110
112
|
self.verbose: int = args.verbose
|
|
111
113
|
self.test_time: float = args.test_time
|
|
112
114
|
if self.test_time < 10.0:
|
|
@@ -132,7 +134,6 @@ class Benchmarking(InsertsArguments):
|
|
|
132
134
|
"-m",
|
|
133
135
|
"--mem-target",
|
|
134
136
|
default="all",
|
|
135
|
-
nargs="?",
|
|
136
137
|
choices=["L1", "L2", "L3", "DRAM", "all"],
|
|
137
138
|
help="Target memory level for 'memory' and 'mixed' tests (Default: all)",
|
|
138
139
|
)
|
|
@@ -140,7 +141,6 @@ class Benchmarking(InsertsArguments):
|
|
|
140
141
|
"-o",
|
|
141
142
|
"--num-ops",
|
|
142
143
|
default=32 * 1024,
|
|
143
|
-
nargs="?",
|
|
144
144
|
type=positive_int,
|
|
145
145
|
help="Number of arithmetic operations to perform in the arithmetic test (Default: 32768)",
|
|
146
146
|
)
|
|
@@ -148,7 +148,6 @@ class Benchmarking(InsertsArguments):
|
|
|
148
148
|
"-d",
|
|
149
149
|
"--data-type",
|
|
150
150
|
default=DataType.f32,
|
|
151
|
-
nargs="?",
|
|
152
151
|
action=enum_action(DataType),
|
|
153
152
|
help="Data type for benchmark operations (Default: f32)",
|
|
154
153
|
)
|
|
@@ -171,7 +170,6 @@ class Benchmarking(InsertsArguments):
|
|
|
171
170
|
parser.add_argument(
|
|
172
171
|
"--ld-st-ratio",
|
|
173
172
|
default=LoadStoreRatio(2, 1),
|
|
174
|
-
nargs="?",
|
|
175
173
|
type=ld_st_ratio_type,
|
|
176
174
|
help="Load-to-store ratio for memory access patterns. Format: 'LD:ST' (e.g., '2:1') or a single "
|
|
177
175
|
"integer for 'N:1' ratio. Use '1:0' for load-only or '0:1' for store-only tests. (Default: 2:1)",
|
|
@@ -179,22 +177,22 @@ class Benchmarking(InsertsArguments):
|
|
|
179
177
|
parser.add_argument(
|
|
180
178
|
"--arith-mem-ratio",
|
|
181
179
|
default=2,
|
|
182
|
-
nargs="?",
|
|
183
180
|
type=positive_int,
|
|
184
181
|
help="Ratio between arithmetic and memory operations for 'mixed' test (Default: 2)",
|
|
185
182
|
)
|
|
186
183
|
parser.add_argument(
|
|
187
184
|
"--mem-test-sizes",
|
|
188
|
-
nargs="
|
|
185
|
+
nargs="+",
|
|
189
186
|
type=mem_test_size_type,
|
|
190
|
-
help="Size of the test arrays for each memory level
|
|
191
|
-
"
|
|
192
|
-
"
|
|
187
|
+
help="Size of the test arrays for each memory level. Accepts binary unit strings like '16KiB', '2MiB', "
|
|
188
|
+
"'8GiB', or 'auto' to automatically size based on detected caches. "
|
|
189
|
+
"e.g. '--mem-test-sizes 16KiB 256KiB 2MiB 64GiB' / "
|
|
190
|
+
"'--mem-test-sizes auto auto 2MiB auto' for L1, L2, L3, DRAM respectively. "
|
|
191
|
+
"(Default: auto for all levels)",
|
|
193
192
|
)
|
|
194
193
|
parser.add_argument(
|
|
195
194
|
"--test-time",
|
|
196
195
|
default=25.0,
|
|
197
|
-
nargs="?",
|
|
198
196
|
type=positive_float,
|
|
199
197
|
help="Target runtime for each individual microbenchmark, in seconds. Low runtime may lead to inaccurate "
|
|
200
198
|
"results. (Default: 25.0)",
|
|
@@ -9,7 +9,6 @@ This module coordinates the entire benchmark pipeline:
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import tempfile
|
|
13
12
|
from pathlib import Path
|
|
14
13
|
from typing import TYPE_CHECKING
|
|
15
14
|
|
|
@@ -20,6 +19,7 @@ from test_bench.builder import (
|
|
|
20
19
|
create_microbenchmark_header,
|
|
21
20
|
run_microbenchmarks,
|
|
22
21
|
)
|
|
22
|
+
from workspace import workspace_context
|
|
23
23
|
|
|
24
24
|
from .benchmark import BaseBenchmark
|
|
25
25
|
from .result import parse_benchmark_output
|
|
@@ -120,9 +120,13 @@ def run_full_benchmark(
|
|
|
120
120
|
|
|
121
121
|
debug(f"Flattened benchmarks for compilation: {list(flat_benchmarks.keys())}")
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
keep_workspace = context.run_config.dry_run or context.run_config.keep_artifacts
|
|
124
|
+
with workspace_context(keep=keep_workspace, prefix="carm-benchmark-") as workspace_dir:
|
|
124
125
|
workspace = Path(workspace_dir)
|
|
125
126
|
|
|
127
|
+
if keep_workspace:
|
|
128
|
+
detail(f"Artifacts will be kept in: {workspace}")
|
|
129
|
+
|
|
126
130
|
# Keep generated artifacts in a writable temporary workspace.
|
|
127
131
|
# Static C sources remain in-package and are included explicitly at compile time.
|
|
128
132
|
generated_header = workspace / "microbenchmarks.h"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections import defaultdict
|
|
6
|
+
from collections.abc import Iterator
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
@@ -68,6 +69,11 @@ class MemoryBenchmarkSuite(ISABenchmarkSuite):
|
|
|
68
69
|
|
|
69
70
|
previous_size_per_thread: Bytes | None = None
|
|
70
71
|
|
|
72
|
+
# Iterator over user-provided per-level sizes (if any)
|
|
73
|
+
user_sizes: Iterator[Bytes | None] | None = None
|
|
74
|
+
if benchmark.mem_test_sizes is not None:
|
|
75
|
+
user_sizes = iter(benchmark.mem_test_sizes)
|
|
76
|
+
|
|
71
77
|
mem_level_indices = architecture.memory_topology.available_cache_levels()
|
|
72
78
|
# Generate memory benchmarks for each memory level
|
|
73
79
|
for mem_level_idx, mem_level_info in zip(mem_level_indices, architecture.memory_topology):
|
|
@@ -76,7 +82,7 @@ class MemoryBenchmarkSuite(ISABenchmarkSuite):
|
|
|
76
82
|
# Plan thread affinity for this memory level
|
|
77
83
|
thread_affinity = architecture.memory_topology.plan_thread_affinity(benchmark.threads, mem_level_idx)
|
|
78
84
|
avail_size_per_thread: Bytes = thread_affinity.total_cache_bytes // thread_affinity.num_threads
|
|
79
|
-
|
|
85
|
+
_is_first_target = mem_level_idx == mem_level_indices[0]
|
|
80
86
|
is_final_target = mem_level_idx == mem_level_indices[-1]
|
|
81
87
|
|
|
82
88
|
if is_final_target and previous_size_per_thread is not None:
|
|
@@ -84,6 +90,13 @@ class MemoryBenchmarkSuite(ISABenchmarkSuite):
|
|
|
84
90
|
else:
|
|
85
91
|
target_size_per_thread = avail_size_per_thread * _CACHE_COVERAGE
|
|
86
92
|
|
|
93
|
+
# Override with user-provided size (if any) for this cache level.
|
|
94
|
+
# A None entry in the list (from "auto") keeps the computed value.
|
|
95
|
+
if user_sizes is not None:
|
|
96
|
+
user_size = next(user_sizes, None)
|
|
97
|
+
if user_size is not None:
|
|
98
|
+
target_size_per_thread = user_size
|
|
99
|
+
|
|
87
100
|
# Warn if the per-thread dataset also fits in a smaller (closer-to-CPU) cache level.
|
|
88
101
|
# E.g. targeting L3 with 16 threads, but per-thread data fits in each thread's L2:
|
|
89
102
|
# the hardware will serve requests from L2, not L3, skewing the bandwidth result.
|
|
@@ -99,7 +112,8 @@ class MemoryBenchmarkSuite(ISABenchmarkSuite):
|
|
|
99
112
|
)
|
|
100
113
|
|
|
101
114
|
# Generate memory benchmark for this level
|
|
102
|
-
|
|
115
|
+
# Use split layout only for the DRAM
|
|
116
|
+
layout_mode = MemoryLayoutMode.split if is_final_target else MemoryLayoutMode.single
|
|
103
117
|
params = MemoryBenchmarkParams(
|
|
104
118
|
data_type=data_type,
|
|
105
119
|
thread_affinity=thread_affinity.cpu_ids,
|
|
@@ -65,7 +65,7 @@ class RooflineBenchmarkSuite(ISABenchmarkSuite):
|
|
|
65
65
|
for bench in self.get_memory_benchmarks().values():
|
|
66
66
|
if bench.cache_level is None:
|
|
67
67
|
raise ValueError(
|
|
68
|
-
f"Memory benchmark {bench.name} missing cache level annotation for roofline
|
|
68
|
+
f"Memory benchmark {bench.name} missing cache level annotation for roofline benchmarki>ng"
|
|
69
69
|
)
|
|
70
70
|
if bench.results is None:
|
|
71
71
|
raise ValueError(f"Memory benchmark {bench.name} missing bandwidth result for roofline benchmarking")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# PYTHON_ARGCOMPLETE_OK
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import argparse
|
|
@@ -9,6 +10,7 @@ from dataclasses import dataclass
|
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import cast
|
|
11
12
|
|
|
13
|
+
import argcomplete
|
|
12
14
|
from rich_argparse import RichHelpFormatter
|
|
13
15
|
|
|
14
16
|
from architecture import set_execution_interface
|
|
@@ -16,9 +18,10 @@ from arguments import InsertsArguments, TopLevelHelpFormatter
|
|
|
16
18
|
from benchmark.interface import run_full_benchmark
|
|
17
19
|
from benchmark.output import output_benchmark_results
|
|
18
20
|
from context import Architecture, Benchmarking, CARMContext, ExecutionInterface, RunConfig
|
|
19
|
-
from error import
|
|
21
|
+
from error import UserError
|
|
20
22
|
from gui.config import GUIConfig
|
|
21
|
-
from output_utils import configure_verbosity, debug, detail, error, info
|
|
23
|
+
from output_utils import configure_verbosity, debug, detail, error, info
|
|
24
|
+
from profiling import ProfileConfig, profile_main
|
|
22
25
|
|
|
23
26
|
RichHelpFormatter.styles.update(
|
|
24
27
|
{
|
|
@@ -45,38 +48,27 @@ def _handle_benchmark(args: argparse.Namespace) -> int:
|
|
|
45
48
|
info(f"Generated template TOML configuration: {args.emit_config}")
|
|
46
49
|
return 0
|
|
47
50
|
except OSError as e:
|
|
48
|
-
|
|
49
|
-
return 1
|
|
51
|
+
raise OSError(f"Failed to write template config: {e}") from e
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
run_config = RunConfig(args)
|
|
58
|
-
|
|
59
|
-
context = CARMContext(
|
|
60
|
-
architecture=architecture, benchmarking=benchmarking, run_config=run_config, exec_interface=exec_iface
|
|
61
|
-
)
|
|
53
|
+
exec_iface = ExecutionInterface(args)
|
|
54
|
+
# Make exec_iface globally available for architecture feature detection
|
|
55
|
+
set_execution_interface(exec_iface)
|
|
56
|
+
architecture = Architecture(args)
|
|
57
|
+
benchmarking = Benchmarking(args)
|
|
58
|
+
run_config = RunConfig(args)
|
|
62
59
|
|
|
63
|
-
|
|
60
|
+
context = CARMContext(
|
|
61
|
+
architecture=architecture, benchmarking=benchmarking, run_config=run_config, exec_interface=exec_iface
|
|
62
|
+
)
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
detail("Dry run finished: benchmark code generated successfully.")
|
|
67
|
-
return 0
|
|
64
|
+
benchmark_suites = run_full_benchmark(context)
|
|
68
65
|
|
|
69
|
-
|
|
66
|
+
if run_config.dry_run:
|
|
67
|
+
detail("Dry run finished: benchmark code generated successfully.")
|
|
70
68
|
return 0
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
debug(str(traceback.format_exc()))
|
|
75
|
-
return 1
|
|
76
|
-
except Exception as e:
|
|
77
|
-
error(f"Unexpected error: {e.__class__.__name__}: {e}")
|
|
78
|
-
debug(str(traceback.format_exc()))
|
|
79
|
-
return 1
|
|
70
|
+
output_benchmark_results(context, benchmark_suites)
|
|
71
|
+
return 0
|
|
80
72
|
|
|
81
73
|
|
|
82
74
|
def _handle_gui(args: argparse.Namespace) -> int:
|
|
@@ -93,15 +85,15 @@ def _handle_gui(args: argparse.Namespace) -> int:
|
|
|
93
85
|
error('Failed to import GUI dependencies. Install optional GUI extras with: pip install "carm-roofline[gui]"')
|
|
94
86
|
debug(str(traceback.format_exc()))
|
|
95
87
|
return 1
|
|
96
|
-
except Exception as e:
|
|
97
|
-
error(f"Unexpected error: {e.__class__.__name__}: {e}")
|
|
98
|
-
debug(str(traceback.format_exc()))
|
|
99
|
-
return 1
|
|
100
88
|
|
|
101
89
|
|
|
102
|
-
def _handle_profile(
|
|
103
|
-
|
|
104
|
-
|
|
90
|
+
def _handle_profile(args: argparse.Namespace) -> int:
|
|
91
|
+
configure_verbosity(args.verbose)
|
|
92
|
+
args_str = ", ".join(f"{k}={v}" for k, v in vars(args).items() if v is not None)
|
|
93
|
+
debug(f"parsed profile arguments: {args_str}")
|
|
94
|
+
|
|
95
|
+
config = ProfileConfig(args)
|
|
96
|
+
return profile_main(config)
|
|
105
97
|
|
|
106
98
|
|
|
107
99
|
@dataclass
|
|
@@ -130,10 +122,12 @@ SUBPARSERS = (
|
|
|
130
122
|
),
|
|
131
123
|
SubParserInfo(
|
|
132
124
|
name="profile",
|
|
133
|
-
help_short="Profile applications
|
|
134
|
-
help_long="Profile applications
|
|
125
|
+
help_short="Profile applications with PAPI",
|
|
126
|
+
help_long="Profile instrumented applications to compute roofline metrics (AI, GFLOP/s, bandwidth). "
|
|
127
|
+
"Supports MPI, threaded, and hybrid applications. "
|
|
128
|
+
"Usage: carm profile [options] -- <command>",
|
|
135
129
|
handler=_handle_profile,
|
|
136
|
-
arg_inserting_modules=(),
|
|
130
|
+
arg_inserting_modules=(ProfileConfig,),
|
|
137
131
|
),
|
|
138
132
|
)
|
|
139
133
|
|
|
@@ -168,6 +162,8 @@ def _create_parser() -> argparse.ArgumentParser:
|
|
|
168
162
|
|
|
169
163
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
170
164
|
parser = _create_parser()
|
|
165
|
+
argcomplete.autocomplete(parser)
|
|
166
|
+
|
|
171
167
|
if argv is None:
|
|
172
168
|
argv = sys.argv[1:]
|
|
173
169
|
|
|
@@ -181,7 +177,17 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
181
177
|
return 2
|
|
182
178
|
|
|
183
179
|
handler = cast(Callable[[argparse.Namespace], int], args.handler)
|
|
184
|
-
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
return handler(args)
|
|
183
|
+
except UserError as e:
|
|
184
|
+
error(str(e))
|
|
185
|
+
debug(str(traceback.format_exc()))
|
|
186
|
+
return 1
|
|
187
|
+
except Exception as e:
|
|
188
|
+
error(f"Unexpected error: {e.__class__.__name__}: {e}")
|
|
189
|
+
debug(str(traceback.format_exc()))
|
|
190
|
+
return 1
|
|
185
191
|
|
|
186
192
|
|
|
187
193
|
if __name__ == "__main__":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: carm-roofline
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1.dev0
|
|
4
4
|
Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
|
|
5
5
|
Author: CHaMP Team
|
|
6
6
|
License: GNU LESSER GENERAL PUBLIC LICENSE
|
|
@@ -518,7 +518,7 @@ Classifier: Intended Audience :: Science/Research
|
|
|
518
518
|
Classifier: Topic :: Software Development :: Testing
|
|
519
519
|
Classifier: Topic :: System :: Benchmark
|
|
520
520
|
Classifier: Programming Language :: Python :: 3
|
|
521
|
-
Classifier: Programming Language :: Python :: 3.
|
|
521
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
522
522
|
Classifier: Operating System :: POSIX :: Linux
|
|
523
523
|
Requires-Python: >=3.9
|
|
524
524
|
Description-Content-Type: text/markdown
|
|
@@ -529,6 +529,7 @@ Requires-Dist: numpy>=2.0.0
|
|
|
529
529
|
Requires-Dist: matplotlib>=3.7.2
|
|
530
530
|
Requires-Dist: tomli>=2.0.0
|
|
531
531
|
Requires-Dist: platformdirs>=4.0.0
|
|
532
|
+
Requires-Dist: argcomplete>=3.0.0
|
|
532
533
|
Provides-Extra: gui
|
|
533
534
|
Requires-Dist: dash[diskcache]; extra == "gui"
|
|
534
535
|
Requires-Dist: dash-bootstrap-components; extra == "gui"
|
|
@@ -11,6 +11,7 @@ results_paths.py
|
|
|
11
11
|
run_config.py
|
|
12
12
|
units.py
|
|
13
13
|
utils.py
|
|
14
|
+
workspace.py
|
|
14
15
|
architecture/__init__.py
|
|
15
16
|
architecture/architecture.py
|
|
16
17
|
architecture/arm.py
|
|
@@ -76,6 +77,14 @@ gui/assets/CARM_icon.svg
|
|
|
76
77
|
gui/assets/CHAMP_logo.svg
|
|
77
78
|
gui/assets/menu_icon.png
|
|
78
79
|
gui/assets/style.css
|
|
80
|
+
profiling/__init__.py
|
|
81
|
+
profiling/aggregation.py
|
|
82
|
+
profiling/backends.py
|
|
83
|
+
profiling/config.py
|
|
84
|
+
profiling/loaders.py
|
|
85
|
+
profiling/metrics.py
|
|
86
|
+
profiling/model.py
|
|
87
|
+
profiling/output.py
|
|
79
88
|
test/test_isa_codegen_integration.py
|
|
80
89
|
test/test_memory_suite_generation.py
|
|
81
90
|
test/test_output_module.py
|