carm-roofline 1.0.2.dev0__tar.gz → 1.0.2.dev1__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.dev1}/PKG-INFO +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/identity.py +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/arithmetic.py +6 -6
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/jsonl.py +12 -12
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/memory.py +2 -2
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/memory_sweep.py +5 -5
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/roofline.py +6 -8
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/memory_sweep.py +3 -3
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm.py +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/PKG-INFO +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/style.css +16 -42
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/components.py +7 -39
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/data.py +33 -21
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/factory.py +13 -56
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/ids.py +2 -10
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/output_utils.py +13 -20
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/aggregation.py +93 -38
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/config.py +2 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/shared.py +3 -3
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/pyproject.toml +1 -1
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_gui_data.py +69 -17
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/units.py +8 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/LICENSE +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/README.md +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/architecture.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/arm.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/detect.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/frequency.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/memory.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/riscv.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/features.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/vlen.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/frequency.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/rvv_version.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/vlen.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/cache.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/features.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/x86_avx512/frequency.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/x86.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/arguments.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/benchmark.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/benchmarking.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/arm.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/data_type.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/instruction.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/operation.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/register.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/isa.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/parameters.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/riscv.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/x86.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/interface.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/base.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/common.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/mixed.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/result.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/arithmetic.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/base.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/memory.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/mixed.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/roofline.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/SOURCES.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/dependency_links.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/entry_points.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/requires.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/top_level.txt +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/context.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/error.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/exec_interface.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/CARM_icon.svg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/CHAMP_logo.svg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/menu_icon.png +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/backends.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/model.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/output.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_backend.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_loader.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_metrics.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_backend.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_loader.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_metrics.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/results_paths.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/roofline_assembly.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/run_config.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/setup.cfg +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_isa_codegen_integration.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_memory_suite_generation.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_output_module.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_roofline_assembly.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_typed_benchmarks.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/__init__.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/builder.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/test_bench.c +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/test_bench.h +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/wrapper.inl +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/utils.py +0 -0
- {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/workspace.py +0 -0
|
@@ -112,7 +112,7 @@ def _levels_from_topology(topology: MemoryTopologyLike | None) -> tuple[MemoryLe
|
|
|
112
112
|
levels.append(
|
|
113
113
|
MemoryLevelSignature(
|
|
114
114
|
name=lvl.name,
|
|
115
|
-
size_bytes=int(lvl.size
|
|
115
|
+
size_bytes=int(lvl.size),
|
|
116
116
|
instances=lvl.instances,
|
|
117
117
|
num_sharing_threads=lvl.num_sharing_threads,
|
|
118
118
|
)
|
|
@@ -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
|
|
@@ -126,7 +126,7 @@ SUBPARSERS = (
|
|
|
126
126
|
),
|
|
127
127
|
SubParserInfo(
|
|
128
128
|
name="profile",
|
|
129
|
-
help_short="Profile applications
|
|
129
|
+
help_short="Profile applications for CARM analysis",
|
|
130
130
|
help_long="Profile instrumented applications to compute roofline metrics (AI, GFLOP/s, bandwidth). "
|
|
131
131
|
"Supports MPI, threaded, and hybrid applications. "
|
|
132
132
|
"Usage: carm profile [options] -- <command>",
|
|
@@ -214,10 +214,24 @@
|
|
|
214
214
|
color: #555;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
.settings-toggle-row {
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
justify-content: space-between;
|
|
223
|
+
padding: 8px 12px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.settings-toggle-label {
|
|
227
|
+
font-size: 13px;
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
color: #555;
|
|
219
230
|
}
|
|
220
231
|
|
|
232
|
+
.normalize-toggle {
|
|
233
|
+
transform: scale(0.8);
|
|
234
|
+
}
|
|
221
235
|
|
|
222
236
|
/* ── Action buttons ────────────────────────────────────────────────────────── */
|
|
223
237
|
.btn-add-roof {
|
|
@@ -238,46 +252,6 @@
|
|
|
238
252
|
background: #0069d9;
|
|
239
253
|
}
|
|
240
254
|
|
|
241
|
-
/* ── Data selection panel ──────────────────────────────────────────────────── */
|
|
242
|
-
.selection-actions {
|
|
243
|
-
display: flex;
|
|
244
|
-
gap: 6px;
|
|
245
|
-
padding: 0 12px 8px;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.btn-select-all,
|
|
249
|
-
.btn-deselect-all {
|
|
250
|
-
flex: 1;
|
|
251
|
-
padding: 4px 8px;
|
|
252
|
-
background: #f0f0f0;
|
|
253
|
-
border: 1px solid #ccc;
|
|
254
|
-
border-radius: 3px;
|
|
255
|
-
font-size: 11px;
|
|
256
|
-
cursor: pointer;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.btn-select-all:hover,
|
|
260
|
-
.btn-deselect-all:hover {
|
|
261
|
-
background: #e0e0e0;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.selection-roof-row {
|
|
265
|
-
padding: 4px 12px;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
.selection-roof-header {
|
|
269
|
-
display: flex;
|
|
270
|
-
align-items: center;
|
|
271
|
-
gap: 6px;
|
|
272
|
-
padding: 4px 0;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.selection-roof-label {
|
|
276
|
-
font-size: 13px;
|
|
277
|
-
font-weight: 600;
|
|
278
|
-
color: #333;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
255
|
|
|
282
256
|
/* ── Plot area ─────────────────────────────────────────────────────────────── */
|
|
283
257
|
.plot-area {
|
|
@@ -18,7 +18,6 @@ from gui.ids import (
|
|
|
18
18
|
NavbarID,
|
|
19
19
|
PlotAreaID,
|
|
20
20
|
RoofCardID,
|
|
21
|
-
SelectionRowID,
|
|
22
21
|
SettingsPanelID,
|
|
23
22
|
SidebarID,
|
|
24
23
|
StoreID,
|
|
@@ -260,11 +259,6 @@ def build_roof_card(
|
|
|
260
259
|
className="apps-section-header",
|
|
261
260
|
children=[
|
|
262
261
|
html.Span("Applications", className="apps-section-title"),
|
|
263
|
-
dbc.Switch(
|
|
264
|
-
id=_make_id(RoofCardID.SWITCH_APPS, index=index),
|
|
265
|
-
value=roof.apps_enabled,
|
|
266
|
-
className="apps-toggle",
|
|
267
|
-
),
|
|
268
262
|
],
|
|
269
263
|
),
|
|
270
264
|
_multi_dropdown(
|
|
@@ -286,47 +280,21 @@ def build_roof_card(
|
|
|
286
280
|
|
|
287
281
|
|
|
288
282
|
def build_settings_panel(store: RoofStore, options: FilterOptions | None = None) -> html.Div:
|
|
289
|
-
"""Settings panel with
|
|
283
|
+
"""Settings panel with the normalize-by-threads toggle."""
|
|
290
284
|
return html.Div(
|
|
291
285
|
className="settings-panel",
|
|
292
286
|
style={"display": "block"} if store.active_panel == ActivePanel.SETTINGS else {"display": "none"},
|
|
293
287
|
children=[
|
|
294
288
|
html.H5("Settings", className="panel-header"),
|
|
295
289
|
html.Div(
|
|
296
|
-
className="
|
|
290
|
+
className="settings-toggle-row",
|
|
297
291
|
children=[
|
|
298
|
-
html.
|
|
299
|
-
|
|
300
|
-
id=SettingsPanelID.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
),
|
|
304
|
-
html.Button(
|
|
305
|
-
"Deselect All",
|
|
306
|
-
id=SettingsPanelID.BTN_DESELECT_ALL,
|
|
307
|
-
className="btn-deselect-all",
|
|
308
|
-
n_clicks=0,
|
|
309
|
-
),
|
|
310
|
-
],
|
|
311
|
-
),
|
|
312
|
-
*[_selection_roof_row(r, i) for i, r in enumerate(store.roofs)],
|
|
313
|
-
],
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
def _selection_roof_row(roof: RoofConfig, roof_idx: int) -> html.Div:
|
|
318
|
-
"""Roof row (checkbox + label) with app toggle."""
|
|
319
|
-
return html.Div(
|
|
320
|
-
className="selection-roof-row",
|
|
321
|
-
children=[
|
|
322
|
-
html.Div(
|
|
323
|
-
className="selection-roof-header",
|
|
324
|
-
children=[
|
|
325
|
-
dbc.Checkbox(
|
|
326
|
-
id=_make_id(SelectionRowID.CHECKBOX_ROOF_VIS, index=roof_idx),
|
|
327
|
-
value=roof.apps_enabled,
|
|
292
|
+
html.Span("Normalize performance by threads", className="settings-toggle-label"),
|
|
293
|
+
dbc.Switch(
|
|
294
|
+
id=SettingsPanelID.SWITCH_NORMALIZE,
|
|
295
|
+
value=store.normalize_by_threads,
|
|
296
|
+
className="normalize-toggle",
|
|
328
297
|
),
|
|
329
|
-
html.Span(roof.label, className="selection-roof-label"),
|
|
330
298
|
],
|
|
331
299
|
),
|
|
332
300
|
],
|
|
@@ -52,7 +52,6 @@ class RoofConfig:
|
|
|
52
52
|
load_store_ratio: str | None = "2:1"
|
|
53
53
|
collapsed: bool = False
|
|
54
54
|
app_ids: list[str] = field(default_factory=list)
|
|
55
|
-
apps_enabled: bool = True
|
|
56
55
|
|
|
57
56
|
def __init__(
|
|
58
57
|
self,
|
|
@@ -65,7 +64,6 @@ class RoofConfig:
|
|
|
65
64
|
compute_insts: list[str] | None = None,
|
|
66
65
|
load_store_ratio: str | None = "2:1",
|
|
67
66
|
app_ids: list[str] | None = None,
|
|
68
|
-
apps_enabled: bool = True,
|
|
69
67
|
collapsed: bool = False,
|
|
70
68
|
) -> None:
|
|
71
69
|
self.id = roof_id or uuid.uuid4().hex
|
|
@@ -78,7 +76,6 @@ class RoofConfig:
|
|
|
78
76
|
self.compute_insts = compute_insts or ["fma", "add"]
|
|
79
77
|
self.load_store_ratio = load_store_ratio
|
|
80
78
|
self.app_ids = app_ids or []
|
|
81
|
-
self.apps_enabled = apps_enabled
|
|
82
79
|
|
|
83
80
|
|
|
84
81
|
def make_default_roof(opts: FilterOptions | None = None) -> RoofConfig:
|
|
@@ -106,6 +103,7 @@ class RoofStore:
|
|
|
106
103
|
def __init__(self, roof_template: RoofConfig | None = None) -> None:
|
|
107
104
|
self.roofs: list[RoofConfig] = [roof_template or RoofConfig()]
|
|
108
105
|
self.active_panel: ActivePanel = ActivePanel.CARM_VIEW
|
|
106
|
+
self.normalize_by_threads: bool = False
|
|
109
107
|
|
|
110
108
|
# Roof CRUD
|
|
111
109
|
def add_roof(self, roof_template: RoofConfig | None = None) -> RoofConfig:
|
|
@@ -146,11 +144,11 @@ class RoofStore:
|
|
|
146
144
|
"load_store_ratio": r.load_store_ratio,
|
|
147
145
|
"collapsed": r.collapsed,
|
|
148
146
|
"app_ids": r.app_ids,
|
|
149
|
-
"apps_enabled": r.apps_enabled,
|
|
150
147
|
}
|
|
151
148
|
for r in self.roofs
|
|
152
149
|
],
|
|
153
150
|
"active_panel": self.active_panel,
|
|
151
|
+
"normalize_by_threads": self.normalize_by_threads,
|
|
154
152
|
}
|
|
155
153
|
|
|
156
154
|
@classmethod
|
|
@@ -168,11 +166,11 @@ class RoofStore:
|
|
|
168
166
|
load_store_ratio=r.get("load_store_ratio", "2:1"),
|
|
169
167
|
collapsed=r.get("collapsed", False),
|
|
170
168
|
app_ids=r.get("app_ids", []),
|
|
171
|
-
apps_enabled=r.get("apps_enabled", True),
|
|
172
169
|
)
|
|
173
170
|
for r in data.get("roofs", [])
|
|
174
171
|
]
|
|
175
172
|
store.active_panel = ActivePanel(data.get("active_panel", "carm_view"))
|
|
173
|
+
store.normalize_by_threads = data.get("normalize_by_threads", False)
|
|
176
174
|
return store
|
|
177
175
|
|
|
178
176
|
|
|
@@ -203,6 +201,7 @@ def build_roofline_figure(
|
|
|
203
201
|
roofs: list[RoofConfig],
|
|
204
202
|
records: list[BenchmarkRecord],
|
|
205
203
|
applications_by_id: dict[str, ApplicationRecord] | None = None,
|
|
204
|
+
normalize_by_threads: bool = False,
|
|
206
205
|
) -> go.Figure:
|
|
207
206
|
"""Build a Plotly roofline figure from real benchmark records.
|
|
208
207
|
|
|
@@ -221,6 +220,7 @@ def build_roofline_figure(
|
|
|
221
220
|
peak_perf_values: list[float] = []
|
|
222
221
|
|
|
223
222
|
for roof in roofs:
|
|
223
|
+
roof_divisor = roof.threads if (normalize_by_threads and roof.threads and roof.threads > 0) else 1
|
|
224
224
|
flt = RooflineFilter(
|
|
225
225
|
machine=roof.machine if roof.machine else None,
|
|
226
226
|
isa=roof.isa if roof.isa else None,
|
|
@@ -237,12 +237,12 @@ def build_roofline_figure(
|
|
|
237
237
|
f"{len(model.source_timestamps)} run(s)"
|
|
238
238
|
)
|
|
239
239
|
if model.peak_performance_by_op:
|
|
240
|
-
peak_perf_values.append(max(p.value for p in model.peak_performance_by_op.values()))
|
|
240
|
+
peak_perf_values.append(max(p.value for p in model.peak_performance_by_op.values()) / roof_divisor)
|
|
241
241
|
rp = model.ridge_points()
|
|
242
242
|
for level, ai_obj in rp.items():
|
|
243
243
|
bw = model.bandwidth_by_level.get(level)
|
|
244
244
|
if bw is not None:
|
|
245
|
-
ridge_pairs.append((ai_obj.value, bw.value))
|
|
245
|
+
ridge_pairs.append((ai_obj.value, bw.value / roof_divisor))
|
|
246
246
|
|
|
247
247
|
# Axis ranges, log10 coordinates (Plotly "log" axis convention).
|
|
248
248
|
if ridge_pairs:
|
|
@@ -262,9 +262,10 @@ def build_roofline_figure(
|
|
|
262
262
|
|
|
263
263
|
for idx, (roof, model) in enumerate(zip(roofs, models)):
|
|
264
264
|
color = _COLORS[idx % len(_COLORS)]
|
|
265
|
+
roof_divisor = roof.threads if (normalize_by_threads and roof.threads and roof.threads > 0) else 1
|
|
265
266
|
|
|
266
267
|
# ── Application points (drawn even when no ceiling data) ─────────
|
|
267
|
-
if applications_by_id
|
|
268
|
+
if applications_by_id:
|
|
268
269
|
for app_id in roof.app_ids:
|
|
269
270
|
rec = applications_by_id.get(app_id)
|
|
270
271
|
if rec is None or not rec.points:
|
|
@@ -272,15 +273,24 @@ def build_roofline_figure(
|
|
|
272
273
|
fig.add_trace(
|
|
273
274
|
go.Scatter(
|
|
274
275
|
x=[p.arithmetic_intensity for p in rec.points],
|
|
275
|
-
y=[
|
|
276
|
+
y=[
|
|
277
|
+
p.flops_per_second
|
|
278
|
+
/ (p.num_threads if normalize_by_threads and p.num_threads and p.num_threads > 0 else 1)
|
|
279
|
+
/ 1e9
|
|
280
|
+
for p in rec.points
|
|
281
|
+
],
|
|
276
282
|
mode="markers",
|
|
277
283
|
name=rec.label,
|
|
278
284
|
legendgroup=roof.id,
|
|
279
285
|
showlegend=True,
|
|
280
286
|
marker={"color": color, "symbol": "circle", "size": 7},
|
|
281
287
|
text=[p.label for p in rec.points],
|
|
288
|
+
customdata=[[p.num_threads] for p in rec.points],
|
|
282
289
|
hovertemplate=(
|
|
283
|
-
f"{rec.label}<br>%{{text}}<br>
|
|
290
|
+
f"{rec.label}<br>%{{text}}<br>"
|
|
291
|
+
f"AI=%{{x:.3f}} OPS/Byte<br>"
|
|
292
|
+
f"Perf=%{{y:.1f}} GOPS/s<br>"
|
|
293
|
+
f"Threads=%{{customdata[0]}}"
|
|
284
294
|
"<extra></extra>"
|
|
285
295
|
),
|
|
286
296
|
)
|
|
@@ -306,24 +316,25 @@ def build_roofline_figure(
|
|
|
306
316
|
)
|
|
307
317
|
continue
|
|
308
318
|
|
|
309
|
-
|
|
319
|
+
peak_perf_raw = max(p.value for p in model.peak_performance_by_op.values()) if has_perf else 0.0
|
|
320
|
+
peak_perf = peak_perf_raw / roof_divisor
|
|
310
321
|
|
|
311
322
|
_first = True
|
|
312
323
|
|
|
313
|
-
# ── Memory bandwidth ceilings (single segment: left edge → ridge point) ──
|
|
314
324
|
for level in ("L1", "L2", "L3", "DRAM"):
|
|
315
325
|
bw = model.bandwidth_by_level.get(level)
|
|
316
326
|
if bw is None:
|
|
317
327
|
continue
|
|
328
|
+
bw_norm = bw / roof_divisor
|
|
318
329
|
ai_left = 1e-6
|
|
319
|
-
y_left =
|
|
330
|
+
y_left = bw_norm.value * ai_left / 1e9
|
|
320
331
|
if peak_perf > 0:
|
|
321
|
-
ridge_ai =
|
|
332
|
+
ridge_ai = peak_perf_raw / bw.value
|
|
322
333
|
ai_right = ridge_ai
|
|
323
334
|
y_right = peak_perf / 1e9
|
|
324
335
|
else:
|
|
325
336
|
ai_right = 1e6
|
|
326
|
-
y_right =
|
|
337
|
+
y_right = bw_norm.value * ai_right / 1e9
|
|
327
338
|
|
|
328
339
|
style = _BW_LINE_STYLES.get(level, {"dash": "solid", "width": 1})
|
|
329
340
|
fig.add_trace(
|
|
@@ -331,7 +342,7 @@ def build_roofline_figure(
|
|
|
331
342
|
x=[ai_left, ai_right],
|
|
332
343
|
y=[y_left, y_right],
|
|
333
344
|
mode="lines",
|
|
334
|
-
name=roof.label if _first else f"{roof.label} {level} ({
|
|
345
|
+
name=roof.label if _first else f"{roof.label} {level} ({bw_norm!s})",
|
|
335
346
|
legendgroup=roof.id,
|
|
336
347
|
line={"color": color, **style},
|
|
337
348
|
hoverinfo="skip",
|
|
@@ -342,20 +353,21 @@ def build_roofline_figure(
|
|
|
342
353
|
|
|
343
354
|
# ── Compute-performance ceilings (single segment: left-most ridge → right edge) ──
|
|
344
355
|
for op_name, perf in model.peak_performance_by_op.items():
|
|
345
|
-
|
|
356
|
+
perf_norm = perf / roof_divisor
|
|
357
|
+
gops = perf_norm.value / 1e9
|
|
346
358
|
if model.bandwidth_by_level:
|
|
347
359
|
op_ridge_ai = min(perf.value / bw.value for bw in model.bandwidth_by_level.values())
|
|
348
360
|
compute_x_start = op_ridge_ai
|
|
349
361
|
else:
|
|
350
362
|
compute_x_start = 1e-6
|
|
351
|
-
is_top = perf.value ==
|
|
363
|
+
is_top = perf.value == peak_perf_raw
|
|
352
364
|
line_style = {"dash": "solid", "width": 1.5} if is_top else {"dash": "dot", "width": 2}
|
|
353
365
|
fig.add_trace(
|
|
354
366
|
go.Scatter(
|
|
355
367
|
x=[compute_x_start, 1e6],
|
|
356
368
|
y=[gops, gops],
|
|
357
369
|
mode="lines",
|
|
358
|
-
name=f"{roof.label} {op_name} ({
|
|
370
|
+
name=f"{roof.label} {op_name} ({perf_norm!s})",
|
|
359
371
|
legendgroup=roof.id,
|
|
360
372
|
line={"color": color, **line_style},
|
|
361
373
|
hoverinfo="skip",
|
|
@@ -367,7 +379,7 @@ def build_roofline_figure(
|
|
|
367
379
|
# ── Invisible ridge-point hover markers (one per op x cache level) ──
|
|
368
380
|
if model.bandwidth_by_level and model.peak_performance_by_op:
|
|
369
381
|
for op_name, perf in model.peak_performance_by_op.items():
|
|
370
|
-
gops = perf.value / 1e9
|
|
382
|
+
gops = (perf / roof_divisor).value / 1e9
|
|
371
383
|
for level, bw in model.bandwidth_by_level.items():
|
|
372
384
|
ridge_ai = perf.value / bw.value
|
|
373
385
|
fig.add_trace(
|
|
@@ -381,7 +393,7 @@ def build_roofline_figure(
|
|
|
381
393
|
f"{roof.label} {op_name} x {level}<br>"
|
|
382
394
|
f"AI = {ridge_ai:.2f} OPS/Byte<br>"
|
|
383
395
|
f"Performance = {gops:.1f} GOPS/s<br>"
|
|
384
|
-
f"BW = {bw!s}"
|
|
396
|
+
f"BW = {bw / roof_divisor!s}"
|
|
385
397
|
),
|
|
386
398
|
showlegend=False,
|
|
387
399
|
)
|