carm-roofline 1.0.0.dev3__tar.gz → 1.0.0.dev4__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 (85) hide show
  1. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/PKG-INFO +1 -1
  2. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/benchmarking.py +4 -4
  3. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/arithmetic.py +67 -64
  4. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/roofline.py +24 -5
  5. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/arithmetic.py +12 -26
  6. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/roofline.py +1 -17
  7. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/PKG-INFO +1 -1
  8. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/pyproject.toml +1 -1
  9. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/LICENSE +0 -0
  10. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/README.md +0 -0
  11. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/__init__.py +0 -0
  12. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/architecture.py +0 -0
  13. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/arm.py +0 -0
  14. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/config.py +0 -0
  15. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/detect.py +0 -0
  16. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/frequency.py +0 -0
  17. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/memory.py +0 -0
  18. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/riscv.py +0 -0
  19. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/arm/features.c +0 -0
  20. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/arm/frequency.h +0 -0
  21. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/arm/vlen.c +0 -0
  22. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/frequency.c +0 -0
  23. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/riscv/frequency.h +0 -0
  24. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/riscv/rvv_version.c +0 -0
  25. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/riscv/vlen.c +0 -0
  26. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/x86/cache.c +0 -0
  27. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/x86/features.c +0 -0
  28. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/x86/frequency.h +0 -0
  29. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/tests/x86/x86_avx512/frequency.h +0 -0
  30. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/architecture/x86.py +0 -0
  31. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/arguments.py +0 -0
  32. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/__init__.py +0 -0
  33. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/benchmark.py +0 -0
  34. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/__init__.py +0 -0
  35. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/arm.py +0 -0
  36. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/code_gen/__init__.py +0 -0
  37. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/code_gen/data_type.py +0 -0
  38. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/code_gen/instruction.py +0 -0
  39. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/code_gen/operation.py +0 -0
  40. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/code_gen/register.py +0 -0
  41. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/isa.py +0 -0
  42. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/parameters.py +0 -0
  43. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/riscv.py +0 -0
  44. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/generation/x86.py +0 -0
  45. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/interface.py +0 -0
  46. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/__init__.py +0 -0
  47. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/base.py +0 -0
  48. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/common.py +0 -0
  49. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/memory.py +0 -0
  50. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/memory_sweep.py +0 -0
  51. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/output/mixed.py +0 -0
  52. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/result.py +0 -0
  53. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/__init__.py +0 -0
  54. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/base.py +0 -0
  55. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/memory.py +0 -0
  56. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/memory_sweep.py +0 -0
  57. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/benchmark/suites/mixed.py +0 -0
  58. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm.py +1 -1
  59. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/SOURCES.txt +0 -0
  60. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/dependency_links.txt +0 -0
  61. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/entry_points.txt +0 -0
  62. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/requires.txt +0 -0
  63. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/carm_roofline.egg-info/top_level.txt +0 -0
  64. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/context.py +0 -0
  65. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/error.py +0 -0
  66. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/exec_interface.py +0 -0
  67. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/__init__.py +0 -0
  68. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/assets/CARM_icon.svg +0 -0
  69. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/assets/CHAMP_logo.svg +0 -0
  70. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/assets/menu_icon.png +0 -0
  71. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/assets/style.css +0 -0
  72. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/config.py +0 -0
  73. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/dashboard.py +0 -0
  74. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/gui/gui_utils.py +0 -0
  75. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/output_utils.py +0 -0
  76. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/results_paths.py +0 -0
  77. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/run_config.py +0 -0
  78. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/setup.cfg +0 -0
  79. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/test_bench/__init__.py +0 -0
  80. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/test_bench/builder.py +0 -0
  81. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/test_bench/test_bench.c +0 -0
  82. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/test_bench/test_bench.h +0 -0
  83. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/test_bench/wrapper.inl +0 -0
  84. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/units.py +0 -0
  85. {carm_roofline-1.0.0.dev3 → carm_roofline-1.0.0.dev4}/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: carm-roofline
3
- Version: 1.0.0.dev3
3
+ Version: 1.0.0.dev4
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
@@ -102,7 +102,7 @@ class Benchmarking(InsertsArguments):
102
102
  self.data_type: DataType = args.data_type
103
103
  self.threads: int = args.threads
104
104
  self.interleaved: bool = args.interleaved
105
- self.instruction: ArithmeticOperation = args.instruction
105
+ self.instructions: set[ArithmeticOperation] = set(args.instruction)
106
106
  self.num_ops: Operations = Operations(args.num_ops)
107
107
  self.ld_st_ratio: LoadStoreRatio = args.ld_st_ratio
108
108
  self.arith_mem_ratio: int = args.arith_mem_ratio
@@ -163,10 +163,10 @@ class Benchmarking(InsertsArguments):
163
163
  )
164
164
  parser.add_argument(
165
165
  "--instruction",
166
- default=ArithmeticOperation.fma,
167
- nargs="?",
166
+ default=[ArithmeticOperation.add, ArithmeticOperation.fma],
167
+ nargs="+",
168
168
  action=enum_action(ArithmeticOperation),
169
- help="Arithmetic instruction benchmarked (Default: 'fma')",
169
+ help="Arithmetic instruction(s) to benchmark (Default: add fma)",
170
170
  )
171
171
  parser.add_argument(
172
172
  "--ld-st-ratio",
@@ -29,26 +29,19 @@ def _collect_gops_by_isa(isa_suites: dict[str, ISABenchmarkSuite]) -> dict[str,
29
29
  gops_by_isa: dict[str, float] = {}
30
30
 
31
31
  for isa, suite in sorted(isa_suites.items(), key=lambda kv: kv[0]):
32
- benches: dict[str, Any]
33
- if hasattr(suite, "get_arithmetic_benchmarks"):
34
- benches = suite.get_arithmetic_benchmarks()
35
- else:
36
- benches = getattr(suite, "benchmarks", {})
37
-
38
32
  values: list[float] = []
39
- for bench in benches.values():
40
- res = getattr(bench, "results", None)
41
- if not isinstance(res, ArithmeticBenchmarkResult):
33
+ for bench in suite.get_arithmetic_benchmarks().values():
34
+ if not isinstance(bench.results, ArithmeticBenchmarkResult):
42
35
  continue
43
36
 
44
- gops_value = float(res.performance.value) / 1e9
37
+ gops_value = float(bench.results.performance.value) / 1e9
45
38
  if not math.isfinite(gops_value):
46
39
  warn(f"Skipping invalid GOPS value for ISA {isa}: {gops_value}")
47
40
  continue
48
41
  values.append(gops_value)
49
42
 
50
43
  if values:
51
- gops_by_isa[isa] = sum(values) / len(values)
44
+ gops_by_isa[isa] = max(values)
52
45
 
53
46
  return gops_by_isa
54
47
 
@@ -64,47 +57,53 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
64
57
  isa_cls.name: isa_cls.from_architecture(context.architecture) for isa_cls in context.architecture.isa
65
58
  }
66
59
 
67
- arithmetic_metrics: dict[str, dict[str, Any]] = {}
60
+ arithmetic_metrics: list[dict[str, Any]] = []
68
61
 
69
62
  for isa, suite in sorted(isa_suites.items(), key=lambda kv: kv[0]):
70
63
  frequency = context.architecture.get_frequency_for_isa(isa)
71
- benches = suite.get_arithmetic_benchmarks()
72
- assert len(benches) == 1, "Expected exactly one arithmetic benchmark per ISA for summary output"
73
- benchmark_name, benchmark = next(iter(benches.items()))
74
- res = benchmark.results
75
- assert isinstance(res, ArithmeticBenchmarkResult), "Expected ArithmeticBenchmarkResult for arithmetic summary"
76
-
77
64
  isa_instance = isa_instances.get(isa)
78
65
  if isa_instance is None:
79
66
  warn(f"No ISA instance available for {isa}; skipping metrics")
80
67
  continue
81
68
 
82
- ops_per_inst = isa_instance.ops_per_inst(benchmark.params.data_type, benchmark.params.operation)
83
- total_insts = (benchmark.params.num_ops.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
84
- cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
85
- ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
86
- ops_per_cycle = ops_per_inst * ipc
87
-
88
- arithmetic_metrics[isa] = {
89
- "benchmark": benchmark_name,
90
- "gops": float(res.performance.value) / 1e9,
91
- "gops_display": str(res.performance),
92
- "ipc": ipc,
93
- "frequency_hz": float(frequency.value),
94
- "frequency_display": str(frequency),
95
- "ops_per_instruction": ops_per_inst,
96
- "ops_per_cycle": ops_per_cycle,
97
- "time_seconds": float(res.time_taken.value),
98
- "time_display": str(res.time_taken),
99
- "repetitions": int(res.num_repetitions),
100
- "cycles": float(cycles.value),
101
- "cycles_display": str(cycles),
102
- }
69
+ benches = suite.get_arithmetic_benchmarks()
70
+ for benchmark_name, benchmark in benches.items():
71
+ res = benchmark.results
72
+ assert isinstance(res, ArithmeticBenchmarkResult), (
73
+ "Expected ArithmeticBenchmarkResult for arithmetic summary"
74
+ )
75
+
76
+ ops_per_inst = isa_instance.ops_per_inst(benchmark.params.data_type, benchmark.params.operation)
77
+ total_insts = (benchmark.params.num_ops.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
78
+ cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
79
+ ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
80
+ ops_per_cycle = ops_per_inst * ipc
81
+
82
+ arithmetic_metrics.append(
83
+ {
84
+ "isa": isa,
85
+ "operation": benchmark.params.operation.name,
86
+ "benchmark": benchmark_name,
87
+ "gops": float(res.performance.value) / 1e9,
88
+ "gops_display": str(res.performance),
89
+ "ipc": ipc,
90
+ "frequency_hz": float(frequency.value),
91
+ "frequency_display": str(frequency),
92
+ "ops_per_instruction": ops_per_inst,
93
+ "ops_per_cycle": ops_per_cycle,
94
+ "time_seconds": float(res.time_taken.value),
95
+ "time_display": str(res.time_taken),
96
+ "repetitions": int(res.num_repetitions),
97
+ "cycles": float(cycles.value),
98
+ "cycles_display": str(cycles),
99
+ }
100
+ )
103
101
 
104
102
  table = Table(title="Arithmetic Performance Summary")
105
103
 
106
104
  # normal output columns
107
105
  table.add_column("ISA", style="cyan")
106
+ table.add_column("Op", style="magenta")
108
107
  table.add_column("GOPS", justify="right")
109
108
  table.add_column("IPC", justify="right")
110
109
  table.add_column("Frequency", justify="right")
@@ -116,7 +115,8 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
116
115
  table.add_column("Repetitions", justify="right")
117
116
  table.add_column("Cycles", justify="right")
118
117
 
119
- for isa, metrics in sorted(arithmetic_metrics.items(), key=lambda kv: kv[0]):
118
+ # sort rows by ISA and operation
119
+ for metrics in sorted(arithmetic_metrics, key=lambda row: (row["isa"], row["operation"])):
120
120
  ipc = float(metrics["ipc"])
121
121
 
122
122
  if print_extra:
@@ -130,7 +130,8 @@ def _print_table(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
130
130
  extra_args = ()
131
131
 
132
132
  table.add_row(
133
- isa,
133
+ metrics["isa"],
134
+ metrics["operation"],
134
135
  str(metrics["gops_display"]),
135
136
  f"{ipc:.2f}",
136
137
  str(metrics["frequency_display"]),
@@ -152,34 +153,36 @@ def _write_json(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite])
152
153
 
153
154
  for isa, suite in sorted(isa_suites.items(), key=lambda kv: kv[0]):
154
155
  frequency = context.architecture.get_frequency_for_isa(isa)
155
- benches = suite.get_arithmetic_benchmarks()
156
- assert len(benches) == 1, "Expected exactly one arithmetic benchmark per ISA for JSON output"
157
- benchmark_name, benchmark = next(iter(benches.items()))
158
- res = benchmark.results
159
- assert isinstance(res, ArithmeticBenchmarkResult), "Expected ArithmeticBenchmarkResult for arithmetic JSON"
160
-
161
156
  isa_instance = isa_instances.get(isa)
162
157
  if isa_instance is None:
163
158
  warn(f"No ISA instance available for {isa}; skipping metrics")
164
159
  continue
165
160
 
166
- ops_per_inst = isa_instance.ops_per_inst(benchmark.params.data_type, benchmark.params.operation)
167
- total_insts = (benchmark.params.num_ops.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
168
- cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
169
- ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
170
-
171
- results[isa] = {
172
- "benchmark": benchmark_name,
173
- "operation": benchmark.params.operation.name,
174
- "gops": float(res.performance.value) / 1e9,
175
- "ipc": ipc,
176
- "frequency_hz": float(frequency.value),
177
- "ops_per_instruction": ops_per_inst,
178
- "ops_per_cycle": ops_per_inst * ipc,
179
- "time_seconds": float(res.time_taken.value),
180
- "repetitions": int(res.num_repetitions),
181
- "cycles": float(cycles.value),
182
- }
161
+ benches = suite.get_arithmetic_benchmarks()
162
+ isa_results: dict[str, object] = {}
163
+ for benchmark_name, benchmark in benches.items():
164
+ res = benchmark.results
165
+ assert isinstance(res, ArithmeticBenchmarkResult), "Expected ArithmeticBenchmarkResult for arithmetic JSON"
166
+
167
+ ops_per_inst = isa_instance.ops_per_inst(benchmark.params.data_type, benchmark.params.operation)
168
+ total_insts = (benchmark.params.num_ops.value // ops_per_inst) * res.num_repetitions if ops_per_inst else 0
169
+ cycles = Cycles.from_time_and_frequency(res.time_taken, frequency)
170
+ ipc = 0.0 if cycles.value == 0 else total_insts / cycles.value
171
+
172
+ isa_results[benchmark.params.operation.name] = {
173
+ "benchmark": benchmark_name,
174
+ "operation": benchmark.params.operation.name,
175
+ "gops": float(res.performance.value) / 1e9,
176
+ "ipc": ipc,
177
+ "frequency_hz": float(frequency.value),
178
+ "ops_per_instruction": ops_per_inst,
179
+ "ops_per_cycle": ops_per_inst * ipc,
180
+ "time_seconds": float(res.time_taken.value),
181
+ "repetitions": int(res.num_repetitions),
182
+ "cycles": float(cycles.value),
183
+ }
184
+
185
+ results[isa] = isa_results
183
186
 
184
187
  out_dir = context.run_config.output_dir / "arithmetic"
185
188
  out_dir.mkdir(parents=True, exist_ok=True)
@@ -8,6 +8,7 @@ from datetime import datetime
8
8
  from pathlib import Path
9
9
  from typing import TYPE_CHECKING
10
10
 
11
+ from benchmark.generation.code_gen.operation import ArithmeticOperation
11
12
  from output_utils import info, warn
12
13
  from units import Cycles
13
14
 
@@ -167,7 +168,12 @@ def _collect_memory_metrics(
167
168
  return bandwidth_by_level, ipc_by_level
168
169
 
169
170
 
170
- def _collect_fp_metrics(context: CARMContext, suite: ISABenchmarkSuite, isa_name: str) -> tuple[float, float]:
171
+ def _collect_fp_metrics(
172
+ context: CARMContext,
173
+ suite: ISABenchmarkSuite,
174
+ isa_name: str,
175
+ operation: ArithmeticOperation | None = None,
176
+ ) -> tuple[float, float]:
171
177
  frequency = context.architecture.get_frequency_for_isa(isa_name)
172
178
  fp_gflops = 0.0
173
179
  fp_ipc = 0.0
@@ -179,6 +185,8 @@ def _collect_fp_metrics(context: CARMContext, suite: ISABenchmarkSuite, isa_name
179
185
  for bench in suite.get_arithmetic_benchmarks().values():
180
186
  if bench.results is None:
181
187
  continue
188
+ if operation is not None and bench.params.operation != operation:
189
+ continue
182
190
  fp_gflops = float(bench.results.performance.value) / 1e9
183
191
  ops_per_inst = isa_instance.ops_per_inst(bench.params.data_type, bench.params.operation)
184
192
  total_insts = (
@@ -214,8 +222,19 @@ def _write_csv(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite], o
214
222
 
215
223
  bandwidth_by_level, icycle_by_level = _collect_memory_metrics(context, suite, isa)
216
224
 
217
- fp_inst = context.benchmarking.instruction.name
218
- fp_gflops, fp_icycle = _collect_fp_metrics(context, suite, isa)
225
+ # Select primary instruction: prefer non-fma if available, else fall back to fma
226
+ primary_instruction = next(
227
+ (op for op in context.benchmarking.instructions if op != ArithmeticOperation.fma),
228
+ ArithmeticOperation.fma,
229
+ )
230
+
231
+ fp_inst = primary_instruction.name
232
+ fp_gflops, fp_icycle = _collect_fp_metrics(context, suite, isa, operation=primary_instruction)
233
+
234
+ fma_gflops = 0.0
235
+ fma_icycle = 0.0
236
+ if ArithmeticOperation.fma in context.benchmarking.instructions:
237
+ fma_gflops, fma_icycle = _collect_fp_metrics(context, suite, isa, operation=ArithmeticOperation.fma)
219
238
 
220
239
  def round_helper(x: float) -> str:
221
240
  return f"{x:.3g}" if x < 1 else f"{x:.3f}"
@@ -240,8 +259,8 @@ def _write_csv(context: CARMContext, isa_suites: dict[str, ISABenchmarkSuite], o
240
259
  round_helper(icycle_by_level["DRAM"]),
241
260
  round_helper(fp_gflops),
242
261
  round_helper(fp_icycle),
243
- 0,
244
- 0,
262
+ round_helper(fma_gflops),
263
+ round_helper(fma_icycle),
245
264
  ]
246
265
 
247
266
  secondary_headers = [
@@ -5,8 +5,6 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from units import Performance
9
-
10
8
  from ..benchmark import ArithmeticBenchmark
11
9
  from ..generation import ArithmeticBenchmarkParams
12
10
  from .base import ISABenchmarkSuite
@@ -19,7 +17,7 @@ if TYPE_CHECKING:
19
17
  class ArithmeticBenchmarkSuite(ISABenchmarkSuite):
20
18
  """Suite for arithmetic-only benchmarks (TestType.ARITHMETIC).
21
19
 
22
- Measures peak arithmetic performance for a specific operation.
20
+ Measures peak arithmetic performance across one or more operations.
23
21
  """
24
22
 
25
23
  @classmethod
@@ -61,29 +59,17 @@ class ArithmeticBenchmarkSuite(ISABenchmarkSuite):
61
59
  # This also helps us deal with SMT, ensuring threads are properly distributed across cores
62
60
  thread_affinity = architecture.memory_topology.plan_thread_affinity(benchmark.threads, 1)
63
61
 
64
- # Generate arithmetic benchmark
65
- params = ArithmeticBenchmarkParams(
66
- data_type=benchmark.data_type,
67
- thread_affinity=thread_affinity.cpu_ids,
68
- operation=benchmark.instruction,
69
- num_ops=benchmark.num_ops,
70
- )
71
- spec = isa.generate_arithmetic(params, context)
62
+ for operation in benchmark.instructions:
63
+ params = ArithmeticBenchmarkParams(
64
+ data_type=benchmark.data_type,
65
+ thread_affinity=thread_affinity.cpu_ids,
66
+ operation=operation,
67
+ num_ops=benchmark.num_ops,
68
+ )
69
+ spec = isa.generate_arithmetic(params, context)
72
70
 
73
- # Add to ISA suite using the benchmark's authoritative name
74
- arith_benchmark = ArithmeticBenchmark(params=params, spec=spec)
75
- suite.add_benchmark(arith_benchmark.name, arith_benchmark)
71
+ # Add to ISA suite using the benchmark's authoritative name
72
+ arith_benchmark = ArithmeticBenchmark(params=params, spec=spec)
73
+ suite.add_benchmark(arith_benchmark.name, arith_benchmark)
76
74
 
77
75
  return suite
78
-
79
- def get_peak_performance(self) -> Performance:
80
- """Get peak GOPS from arithmetic portion of roofline suite.
81
-
82
- Returns:
83
- Peak GOPS from arithmetic benchmarks, or None if unavailable.
84
- """
85
- arith_benchmarks = self.get_arithmetic_benchmarks()
86
- if len(arith_benchmarks) > 1:
87
- raise ValueError("Expected exactly one arithmetic benchmark for peak performance calculation")
88
-
89
- return super().get_peak_performance()
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from units import ArithmeticIntensity, Bandwidth, Performance
8
+ from units import ArithmeticIntensity, Bandwidth
9
9
 
10
10
  from .arithmetic import ArithmeticBenchmarkSuite
11
11
  from .base import ISABenchmarkSuite
@@ -53,22 +53,6 @@ class RooflineBenchmarkSuite(ISABenchmarkSuite):
53
53
 
54
54
  return suite
55
55
 
56
- def get_peak_performance(self) -> Performance:
57
- """Get peak GOPS from arithmetic portion of roofline suite.
58
-
59
- Returns:
60
- Peak GOPS from arithmetic benchmarks, or None if unavailable.
61
- """
62
- arith_benchmarks = self.get_arithmetic_benchmarks()
63
- if len(arith_benchmarks) > 1:
64
- raise ValueError("Expected exactly one arithmetic benchmark for peak performance calculation")
65
-
66
- arith_benchmark = next(iter(arith_benchmarks.values()))
67
- if arith_benchmark.results is None:
68
- raise ValueError("Arithmetic benchmark missing performance result for roofline benchmarking")
69
-
70
- return arith_benchmark.results.performance
71
-
72
56
  def get_bandwidth_by_level(self) -> dict[str, Bandwidth]:
73
57
  """Get peak bandwidth for each cache level from memory benchmarks.
74
58
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: carm-roofline
3
- Version: 1.0.0.dev3
3
+ Version: 1.0.0.dev4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "carm-roofline"
3
- version = "1.0.0.dev3"
3
+ version = "1.0.0.dev4"
4
4
  description = "CARM: Cache-Aware Roofline Model benchmarking and visualization toolkit"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
+ #!/usr/bin/env python3
1
2
  from __future__ import annotations
2
3
 
3
- #!/usr/bin/env python3
4
4
  import argparse
5
5
  import sys
6
6
  import traceback