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.
Files changed (110) hide show
  1. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/PKG-INFO +1 -1
  2. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/identity.py +1 -1
  3. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/arithmetic.py +6 -6
  4. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/jsonl.py +12 -12
  5. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/memory.py +2 -2
  6. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/memory_sweep.py +5 -5
  7. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/roofline.py +6 -8
  8. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/memory_sweep.py +3 -3
  9. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm.py +1 -1
  10. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/PKG-INFO +1 -1
  11. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/style.css +16 -42
  12. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/components.py +7 -39
  13. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/data.py +33 -21
  14. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/factory.py +13 -56
  15. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/ids.py +2 -10
  16. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/output_utils.py +13 -20
  17. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/aggregation.py +93 -38
  18. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/config.py +2 -1
  19. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/shared.py +3 -3
  20. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/pyproject.toml +1 -1
  21. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_gui_data.py +69 -17
  22. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/units.py +8 -0
  23. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/LICENSE +0 -0
  24. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/README.md +0 -0
  25. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/__init__.py +0 -0
  26. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/architecture.py +0 -0
  27. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/arm.py +0 -0
  28. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/config.py +0 -0
  29. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/detect.py +0 -0
  30. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/frequency.py +0 -0
  31. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/memory.py +0 -0
  32. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/riscv.py +0 -0
  33. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/features.c +0 -0
  34. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/frequency.h +0 -0
  35. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/arm/vlen.c +0 -0
  36. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/frequency.c +0 -0
  37. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/frequency.h +0 -0
  38. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/rvv_version.c +0 -0
  39. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/riscv/vlen.c +0 -0
  40. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/cache.c +0 -0
  41. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/features.c +0 -0
  42. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/frequency.h +0 -0
  43. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/tests/x86/x86_avx512/frequency.h +0 -0
  44. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/architecture/x86.py +0 -0
  45. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/arguments.py +0 -0
  46. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/__init__.py +0 -0
  47. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/benchmark.py +0 -0
  48. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/benchmarking.py +0 -0
  49. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/__init__.py +0 -0
  50. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/arm.py +0 -0
  51. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/__init__.py +0 -0
  52. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/data_type.py +0 -0
  53. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/instruction.py +0 -0
  54. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/operation.py +0 -0
  55. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/code_gen/register.py +0 -0
  56. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/isa.py +0 -0
  57. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/parameters.py +0 -0
  58. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/riscv.py +0 -0
  59. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/generation/x86.py +0 -0
  60. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/interface.py +0 -0
  61. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/__init__.py +0 -0
  62. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/base.py +0 -0
  63. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/common.py +0 -0
  64. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/output/mixed.py +0 -0
  65. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/result.py +0 -0
  66. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/__init__.py +0 -0
  67. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/arithmetic.py +0 -0
  68. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/base.py +0 -0
  69. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/memory.py +0 -0
  70. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/mixed.py +0 -0
  71. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/benchmark/suites/roofline.py +0 -0
  72. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/SOURCES.txt +0 -0
  73. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/dependency_links.txt +0 -0
  74. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/entry_points.txt +0 -0
  75. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/requires.txt +0 -0
  76. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/carm_roofline.egg-info/top_level.txt +0 -0
  77. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/context.py +0 -0
  78. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/error.py +0 -0
  79. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/exec_interface.py +0 -0
  80. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/__init__.py +0 -0
  81. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/CARM_icon.svg +0 -0
  82. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/CHAMP_logo.svg +0 -0
  83. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/assets/menu_icon.png +0 -0
  84. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/gui/config.py +0 -0
  85. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/__init__.py +0 -0
  86. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/backends.py +0 -0
  87. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/model.py +0 -0
  88. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/output.py +0 -0
  89. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_backend.py +0 -0
  90. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_loader.py +0 -0
  91. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/papi_metrics.py +0 -0
  92. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_backend.py +0 -0
  93. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_loader.py +0 -0
  94. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/profiling/perf_metrics.py +0 -0
  95. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/results_paths.py +0 -0
  96. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/roofline_assembly.py +0 -0
  97. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/run_config.py +0 -0
  98. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/setup.cfg +0 -0
  99. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_isa_codegen_integration.py +0 -0
  100. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_memory_suite_generation.py +0 -0
  101. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_output_module.py +0 -0
  102. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_roofline_assembly.py +0 -0
  103. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test/test_typed_benchmarks.py +0 -0
  104. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/__init__.py +0 -0
  105. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/builder.py +0 -0
  106. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/test_bench.c +0 -0
  107. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/test_bench.h +0 -0
  108. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/test_bench/wrapper.inl +0 -0
  109. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/utils.py +0 -0
  110. {carm_roofline-1.0.2.dev0 → carm_roofline-1.0.2.dev1}/workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: carm-roofline
3
- Version: 1.0.2.dev0
3
+ Version: 1.0.2.dev1
4
4
  Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
5
5
  Author: CHaMP Team
6
6
  License: Apache License
@@ -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.value),
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.value) / 1e9
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.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
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.value) / 1e9,
87
+ "gops": float(res.performance) / 1e9,
88
88
  "gops_display": str(res.performance),
89
89
  "ipc": ipc,
90
- "frequency_hz": float(frequency.value),
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.value),
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.value),
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.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
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.value),
58
- "performance_gops": res.performance.value / 1e9,
57
+ "num_ops": int(bench.params.num_ops),
58
+ "performance_gops": float(res.performance) / 1e9,
59
59
  "ipc": ipc,
60
- "frequency_hz": frequency.value,
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.value,
63
+ "time_seconds": float(res.time_taken),
64
64
  "repetitions": res.num_repetitions,
65
- "cycles": cycles.value,
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.value // bpi) * res.num_repetitions
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.value,
103
- "working_set_bytes": bench.working_set_bytes.value,
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.value / 1e9,
105
+ "bandwidth_gbps": float(res.bandwidth) / 1e9,
106
106
  "ipc": ipc,
107
- "time_seconds": res.time_taken.value,
107
+ "time_seconds": float(res.time_taken),
108
108
  "repetitions": res.num_repetitions,
109
- "cycles": cycles.value,
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.value) / 1e9
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.value // bytes_per_inst) * bench.results.num_repetitions
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.value // bytes_per_inst) * bench.results.num_repetitions
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.value) / 1e9
117
+ bw_val = float(bandwidth) / 1e9
118
118
  if not math.isfinite(bw_val):
119
119
  continue
120
- points.append((ws_bytes.value, cache_level, bw_val))
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.value) / 1e9
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.value, cache_level, bw_val))
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.value // 1024)
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.value)
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.value) / 1e9
179
- total_insts = (bench.working_set_bytes.value // bytes_per_inst) * bench.results.num_repetitions
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.value) / 1e9
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.value)
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.value)
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].value)
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 with PAPI",
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>",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: carm-roofline
3
- Version: 1.0.2.dev0
3
+ Version: 1.0.2.dev1
4
4
  Summary: CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit
5
5
  Author: CHaMP Team
6
6
  License: Apache License
@@ -214,10 +214,24 @@
214
214
  color: #555;
215
215
  }
216
216
 
217
- .apps-toggle {
218
- transform: scale(0.8);
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 roof/app visibility toggles."""
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="selection-actions",
290
+ className="settings-toggle-row",
297
291
  children=[
298
- html.Button(
299
- "Select All",
300
- id=SettingsPanelID.BTN_SELECT_ALL,
301
- className="btn-select-all",
302
- n_clicks=0,
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 and roof.apps_enabled:
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=[p.flops_per_second / 1e9 for p in rec.points],
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>AI=%{{x:.3f}} OPS/Byte<br>Perf=%{{y:.1f}} GOPS/s"
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
- peak_perf = max(p.value for p in model.peak_performance_by_op.values()) if has_perf else 0.0
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 = bw.value * ai_left / 1e9
330
+ y_left = bw_norm.value * ai_left / 1e9
320
331
  if peak_perf > 0:
321
- ridge_ai = peak_perf / bw.value
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 = bw.value * ai_right / 1e9
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} ({bw!s})",
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
- gops = perf.value / 1e9
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 == peak_perf
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} ({perf!s})",
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
  )