iwilldoit commited on
Commit
4beebc3
·
1 Parent(s): 91b1e2c

feat: AST-level Python analysis + MI300X benchmark suite

Browse files

- AST Transformer: Uses Python's ast module for tree-level analysis
- Detects device assignments, CUDA call sites, embedded kernels
- Identifies env mutations and inline kernel strings structurally
- Not regex — real compiler-level understanding of code structure
- MI300X Benchmark Suite: Ready to run on AMD Developer Cloud
- Device info, memory bandwidth, and GEMM TFLOPS tests
- Saves JSON results as proof of AMD GPU usage
- Wired AST findings into analyzer, API, and orchestrator

agents/analyzer.py CHANGED
@@ -19,6 +19,7 @@ from knowledge.cuda_mappings import (
19
  HARDWARE_AWARE_MAPPINGS,
20
  IMPLICIT_CUDA_PATTERNS,
21
  )
 
22
 
23
 
24
  @dataclass
@@ -54,6 +55,7 @@ class AnalysisResult:
54
  migration_level: str = "Easy"
55
  code_type: str = "python" # python, cpp, dockerfile, requirements
56
  summary: Dict = field(default_factory=dict)
 
57
  trace_log: List[str] = field(default_factory=list)
58
 
59
 
@@ -114,6 +116,10 @@ class AnalyzerAgent:
114
  self._scan_implicit_assumptions(lines, code, result)
115
  result.trace_log.append(f"🧪 Exploration Scan → Found {len(result.implicit_assumptions)} implicit CUDA assumptions")
116
 
 
 
 
 
117
  # Build saliency map (per-line risk scoring)
118
  self._build_saliency_map(lines, result)
119
  result.trace_log.append(f"🎯 Saliency Map → {sum(1 for v in result.saliency_map.values() if v == 'critical')} critical lines identified")
@@ -500,6 +506,7 @@ class AnalyzerAgent:
500
  "known_issues": len(result.known_issues),
501
  "hardware_issues": len(result.hardware_issues),
502
  "implicit_assumptions": len(result.implicit_assumptions),
 
503
  "critical_lines": sum(1 for v in result.saliency_map.values() if v == "critical"),
504
  "migration_score": result.migration_score,
505
  "migration_health": result.migration_health,
@@ -509,3 +516,19 @@ class AnalyzerAgent:
509
  "errors": sum(1 for p in result.detected_patterns if p.severity == "error"),
510
  "compatible": sum(1 for p in result.detected_patterns if p.severity == "info"),
511
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  HARDWARE_AWARE_MAPPINGS,
20
  IMPLICIT_CUDA_PATTERNS,
21
  )
22
+ from agents.ast_transformer import ASTTransformer
23
 
24
 
25
  @dataclass
 
55
  migration_level: str = "Easy"
56
  code_type: str = "python" # python, cpp, dockerfile, requirements
57
  summary: Dict = field(default_factory=dict)
58
+ ast_findings: List[Dict] = field(default_factory=list) # AST-level analysis results
59
  trace_log: List[str] = field(default_factory=list)
60
 
61
 
 
116
  self._scan_implicit_assumptions(lines, code, result)
117
  result.trace_log.append(f"🧪 Exploration Scan → Found {len(result.implicit_assumptions)} implicit CUDA assumptions")
118
 
119
+ # AST-level analysis (Python only — real compiler-level understanding)
120
+ if result.code_type == "python":
121
+ self._run_ast_analysis(code, result)
122
+
123
  # Build saliency map (per-line risk scoring)
124
  self._build_saliency_map(lines, result)
125
  result.trace_log.append(f"🎯 Saliency Map → {sum(1 for v in result.saliency_map.values() if v == 'critical')} critical lines identified")
 
506
  "known_issues": len(result.known_issues),
507
  "hardware_issues": len(result.hardware_issues),
508
  "implicit_assumptions": len(result.implicit_assumptions),
509
+ "ast_findings": len(result.ast_findings),
510
  "critical_lines": sum(1 for v in result.saliency_map.values() if v == "critical"),
511
  "migration_score": result.migration_score,
512
  "migration_health": result.migration_health,
 
516
  "errors": sum(1 for p in result.detected_patterns if p.severity == "error"),
517
  "compatible": sum(1 for p in result.detected_patterns if p.severity == "info"),
518
  }
519
+
520
+ def _run_ast_analysis(self, code: str, result: AnalysisResult):
521
+ """
522
+ Run AST-level analysis on Python code using Python's ast module.
523
+ This is a real compiler-level pass — not regex.
524
+ """
525
+ transformer = ASTTransformer()
526
+ findings, trace = transformer.analyze(code)
527
+ result.ast_findings = findings
528
+ result.trace_log.extend(trace)
529
+
530
+ # Count critical AST findings
531
+ critical = sum(1 for f in findings if f.get("severity") == "critical")
532
+ if critical > 0:
533
+ result.trace_log.append(f"🌳 AST: {critical} critical finding(s) — embedded kernels require manual review")
534
+
agents/ast_transformer.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ROCm Forge — AST-Level Python Transformer
3
+ Uses Python's ast module to perform tree-level code transformation,
4
+ going beyond regex to understand code STRUCTURE.
5
+
6
+ This is the critical differentiator from hipify and other string-based tools.
7
+ """
8
+ import ast
9
+ import re
10
+ from typing import List, Tuple, Dict
11
+
12
+
13
+ class CUDANodeVisitor(ast.NodeVisitor):
14
+ """
15
+ AST visitor that walks the Python syntax tree and detects
16
+ CUDA-specific constructs at the structural level.
17
+ """
18
+
19
+ def __init__(self):
20
+ self.findings: List[Dict] = []
21
+ self.device_vars: Dict[str, int] = {} # var_name -> line_no
22
+ self.cuda_call_sites: List[Dict] = []
23
+ self.env_mutations: List[Dict] = []
24
+ self.string_literals_with_cuda: List[Dict] = []
25
+
26
+ def visit_Assign(self, node: ast.Assign):
27
+ """Detect device = torch.device('cuda:0') assignments."""
28
+ if isinstance(node.value, ast.Call):
29
+ func = node.value
30
+ func_name = self._get_func_name(func)
31
+
32
+ # torch.device("cuda:X") pattern
33
+ if func_name == "torch.device" and func.args:
34
+ arg = func.args[0]
35
+ if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
36
+ if "cuda" in arg.value:
37
+ for target in node.targets:
38
+ if isinstance(target, ast.Name):
39
+ self.device_vars[target.id] = node.lineno
40
+ self.findings.append({
41
+ "line": node.lineno,
42
+ "type": "device_assignment",
43
+ "original": f'torch.device("{arg.value}")',
44
+ "suggestion": f'torch.device("{arg.value}") # Works on ROCm — PyTorch ROCm uses cuda namespace',
45
+ "severity": "info",
46
+ })
47
+
48
+ # os.environ["CUDA_VISIBLE_DEVICES"] = ... pattern
49
+ if isinstance(node.value, ast.Constant) and len(node.targets) == 1:
50
+ target = node.targets[0]
51
+ if isinstance(target, ast.Subscript):
52
+ if isinstance(target.slice, ast.Constant) and isinstance(target.slice.value, str):
53
+ if "CUDA" in target.slice.value:
54
+ self.env_mutations.append({
55
+ "line": node.lineno,
56
+ "var": target.slice.value,
57
+ "type": "env_mutation",
58
+ })
59
+
60
+ self.generic_visit(node)
61
+
62
+ def visit_Call(self, node: ast.Call):
63
+ """Detect CUDA API calls: .cuda(), torch.cuda.*, etc."""
64
+ func_name = self._get_func_name(node)
65
+
66
+ # .cuda() method calls
67
+ if func_name and func_name.endswith(".cuda"):
68
+ self.cuda_call_sites.append({
69
+ "line": node.lineno,
70
+ "call": func_name,
71
+ "type": "cuda_method",
72
+ "note": ".cuda() works on ROCm — maps to HIP backend",
73
+ })
74
+
75
+ # torch.cuda.is_available(), torch.cuda.device_count(), etc.
76
+ if func_name and "torch.cuda" in func_name:
77
+ self.cuda_call_sites.append({
78
+ "line": node.lineno,
79
+ "call": func_name,
80
+ "type": "torch_cuda_api",
81
+ "note": "torch.cuda.* APIs work on ROCm via HIP backend",
82
+ })
83
+
84
+ # load_inline / cpp_extension calls
85
+ if func_name and ("load_inline" in func_name or "load" in func_name):
86
+ for kw in node.keywords:
87
+ if kw.arg == "cuda_sources":
88
+ self.findings.append({
89
+ "line": node.lineno,
90
+ "type": "inline_kernel",
91
+ "original": "cuda_sources=[...]",
92
+ "suggestion": "hip_sources=[...] (rename parameter for clarity)",
93
+ "severity": "review",
94
+ })
95
+
96
+ self.generic_visit(node)
97
+
98
+ def visit_Constant(self, node: ast.Constant):
99
+ """Detect string literals containing CUDA code."""
100
+ if isinstance(node.value, str) and len(node.value) > 50:
101
+ cuda_indicators = [
102
+ "cuda_runtime.h", "cudaMalloc", "cudaFree",
103
+ "__global__", "cudaMemcpy", "cudaDeviceSynchronize",
104
+ "<<<", ">>>",
105
+ ]
106
+ if any(ind in node.value for ind in cuda_indicators):
107
+ self.string_literals_with_cuda.append({
108
+ "line": node.lineno,
109
+ "length": len(node.value),
110
+ "type": "embedded_cuda_kernel",
111
+ "note": "Embedded CUDA C/C++ kernel detected in string literal",
112
+ })
113
+ self.generic_visit(node)
114
+
115
+ def visit_If(self, node: ast.If):
116
+ """Detect `if not torch.cuda.is_available()` guards."""
117
+ test_src = ast.dump(node.test)
118
+ if "torch" in test_src and "cuda" in test_src:
119
+ self.findings.append({
120
+ "line": node.lineno,
121
+ "type": "cuda_guard",
122
+ "original": "CUDA availability check",
123
+ "suggestion": "Works on ROCm — torch.cuda.is_available() returns True with HIP backend",
124
+ "severity": "info",
125
+ })
126
+ self.generic_visit(node)
127
+
128
+ def visit_ImportFrom(self, node: ast.ImportFrom):
129
+ """Detect imports from CUDA-specific modules."""
130
+ if node.module and "cuda" in node.module.lower():
131
+ self.findings.append({
132
+ "line": node.lineno,
133
+ "type": "cuda_import",
134
+ "original": f"from {node.module} import ...",
135
+ "suggestion": "Check ROCm compatibility for this import",
136
+ "severity": "review",
137
+ })
138
+ self.generic_visit(node)
139
+
140
+ def _get_func_name(self, node: ast.Call) -> str:
141
+ """Extract dotted function name from a Call node."""
142
+ if isinstance(node.func, ast.Name):
143
+ return node.func.id
144
+ elif isinstance(node.func, ast.Attribute):
145
+ parts = []
146
+ current = node.func
147
+ while isinstance(current, ast.Attribute):
148
+ parts.append(current.attr)
149
+ current = current.value
150
+ if isinstance(current, ast.Name):
151
+ parts.append(current.id)
152
+ return ".".join(reversed(parts))
153
+ return ""
154
+
155
+
156
+ class ASTTransformer:
157
+ """
158
+ AST-level Python code transformer.
159
+ Performs structural analysis beyond regex — understands scope, assignment,
160
+ function calls, and string literal contexts.
161
+ """
162
+
163
+ def __init__(self):
164
+ self.trace_log: List[str] = []
165
+ self.ast_findings: List[Dict] = []
166
+
167
+ def analyze(self, code: str) -> Tuple[List[Dict], List[str]]:
168
+ """
169
+ Perform AST-level analysis on Python code.
170
+ Returns (findings, trace_log).
171
+ Falls back gracefully if code can't be parsed.
172
+ """
173
+ self.trace_log = []
174
+ self.ast_findings = []
175
+
176
+ try:
177
+ tree = ast.parse(code)
178
+ self.trace_log.append("🌳 AST parse successful — performing tree-level analysis")
179
+ except SyntaxError as e:
180
+ self.trace_log.append(f"🌳 AST parse failed (line {e.lineno}) — code contains non-Python syntax, using regex fallback")
181
+ return [], self.trace_log
182
+
183
+ visitor = CUDANodeVisitor()
184
+ visitor.visit(tree)
185
+
186
+ # Aggregate findings
187
+ self.ast_findings = visitor.findings.copy()
188
+
189
+ # Report device variables
190
+ for var, line in visitor.device_vars.items():
191
+ self.ast_findings.append({
192
+ "line": line,
193
+ "type": "device_variable",
194
+ "original": f'{var} = torch.device("cuda:...")',
195
+ "suggestion": "Device variable tracked — all downstream .to(device) calls are AMD-safe",
196
+ "severity": "info",
197
+ })
198
+
199
+ # Report embedded kernels
200
+ for kernel in visitor.string_literals_with_cuda:
201
+ self.ast_findings.append({
202
+ "line": kernel["line"],
203
+ "type": "embedded_kernel",
204
+ "original": f"Inline CUDA kernel ({kernel['length']} chars)",
205
+ "suggestion": "Embedded kernel requires hipify transformation of string contents",
206
+ "severity": "critical",
207
+ })
208
+
209
+ # Report CUDA call sites
210
+ for call in visitor.cuda_call_sites:
211
+ self.ast_findings.append({
212
+ "line": call["line"],
213
+ "type": call["type"],
214
+ "original": call["call"],
215
+ "suggestion": call["note"],
216
+ "severity": "info",
217
+ })
218
+
219
+ # Report env mutations
220
+ for env in visitor.env_mutations:
221
+ self.ast_findings.append({
222
+ "line": env["line"],
223
+ "type": "env_mutation",
224
+ "original": f'os.environ["{env["var"]}"]',
225
+ "suggestion": f'Needs migration to ROCm equivalent env var',
226
+ "severity": "warning",
227
+ })
228
+
229
+ # Summary
230
+ self.trace_log.append(
231
+ f"🌳 AST Analysis: {len(visitor.device_vars)} device vars, "
232
+ f"{len(visitor.cuda_call_sites)} CUDA calls, "
233
+ f"{len(visitor.string_literals_with_cuda)} embedded kernels, "
234
+ f"{len(visitor.env_mutations)} env mutations"
235
+ )
236
+
237
+ return self.ast_findings, self.trace_log
api.py CHANGED
@@ -71,6 +71,7 @@ async def migrate_code(request: MigrationRequest):
71
  } for p in result.analysis.hardware_issues
72
  ],
73
  "implicit_assumptions": result.analysis.implicit_assumptions,
 
74
  "saliency_map": result.analysis.saliency_map,
75
  "detected_patterns": [
76
  {
 
71
  } for p in result.analysis.hardware_issues
72
  ],
73
  "implicit_assumptions": result.analysis.implicit_assumptions,
74
+ "ast_findings": result.analysis.ast_findings,
75
  "saliency_map": result.analysis.saliency_map,
76
  "detected_patterns": [
77
  {
benchmark/rocm_benchmark.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ROCm Forge — MI300X Benchmark Suite
3
+ Ready to run on AMD Developer Cloud when credits arrive.
4
+
5
+ Usage:
6
+ python benchmark/rocm_benchmark.py --all
7
+ python benchmark/rocm_benchmark.py --device-info
8
+ python benchmark/rocm_benchmark.py --memory
9
+ python benchmark/rocm_benchmark.py --compute
10
+ """
11
+ import argparse
12
+ import time
13
+ import json
14
+ import os
15
+ import sys
16
+ from datetime import datetime
17
+
18
+
19
+ def check_rocm_available():
20
+ """Check if ROCm/HIP runtime is available."""
21
+ try:
22
+ import torch
23
+ if not torch.cuda.is_available():
24
+ print("❌ No GPU detected. ROCm/HIP runtime not available.")
25
+ print(" Make sure you're running on an AMD GPU instance with ROCm installed.")
26
+ return False
27
+ device_name = torch.cuda.get_device_name(0)
28
+ print(f"✅ GPU detected: {device_name}")
29
+ print(f" PyTorch version: {torch.__version__}")
30
+ hip_version = getattr(torch.version, 'hip', None)
31
+ cuda_version = getattr(torch.version, 'cuda', None)
32
+ if hip_version:
33
+ print(f" HIP version: {hip_version}")
34
+ elif cuda_version:
35
+ print(f" CUDA version: {cuda_version}")
36
+ return True
37
+ except Exception as e:
38
+ print(f"❌ Error checking GPU: {e}")
39
+ return False
40
+
41
+
42
+ def device_info_benchmark():
43
+ """Collect detailed device information for the hackathon submission."""
44
+ import torch
45
+
46
+ results = {
47
+ "timestamp": datetime.now().isoformat(),
48
+ "pytorch_version": torch.__version__,
49
+ "hip_version": getattr(torch.version, 'hip', 'N/A'),
50
+ "gpu_count": torch.cuda.device_count(),
51
+ "devices": [],
52
+ }
53
+
54
+ for i in range(torch.cuda.device_count()):
55
+ props = torch.cuda.get_device_properties(i)
56
+ device_info = {
57
+ "index": i,
58
+ "name": props.name,
59
+ "total_memory_gb": round(props.total_mem / (1024**3), 2),
60
+ "multi_processor_count": props.multi_processor_count,
61
+ "major": props.major,
62
+ "minor": props.minor,
63
+ }
64
+ results["devices"].append(device_info)
65
+
66
+ print("\n📊 Device Information")
67
+ print("=" * 50)
68
+ print(json.dumps(results, indent=2))
69
+ return results
70
+
71
+
72
+ def memory_benchmark():
73
+ """Test GPU memory allocation and bandwidth."""
74
+ import torch
75
+
76
+ device = torch.device("cuda:0")
77
+ results = {"tests": []}
78
+
79
+ sizes_mb = [256, 512, 1024, 2048, 4096]
80
+ print("\n💾 Memory Benchmark")
81
+ print("=" * 50)
82
+
83
+ for size_mb in sizes_mb:
84
+ n_elements = (size_mb * 1024 * 1024) // 4 # float32
85
+ torch.cuda.synchronize()
86
+
87
+ # Allocation
88
+ start = time.perf_counter()
89
+ tensor = torch.zeros(n_elements, dtype=torch.float32, device=device)
90
+ torch.cuda.synchronize()
91
+ alloc_time = (time.perf_counter() - start) * 1000
92
+
93
+ # Fill (bandwidth test)
94
+ start = time.perf_counter()
95
+ tensor.fill_(1.0)
96
+ torch.cuda.synchronize()
97
+ fill_time = (time.perf_counter() - start) * 1000
98
+ bandwidth_gbps = (size_mb / 1024) / (fill_time / 1000) if fill_time > 0 else 0
99
+
100
+ # Copy D2D
101
+ start = time.perf_counter()
102
+ tensor2 = tensor.clone()
103
+ torch.cuda.synchronize()
104
+ copy_time = (time.perf_counter() - start) * 1000
105
+
106
+ result = {
107
+ "size_mb": size_mb,
108
+ "alloc_ms": round(alloc_time, 2),
109
+ "fill_ms": round(fill_time, 2),
110
+ "bandwidth_gbps": round(bandwidth_gbps, 2),
111
+ "copy_d2d_ms": round(copy_time, 2),
112
+ }
113
+ results["tests"].append(result)
114
+ print(f" {size_mb:>5} MB → alloc: {alloc_time:6.2f}ms fill: {fill_time:6.2f}ms bandwidth: {bandwidth_gbps:7.2f} GB/s D2D: {copy_time:6.2f}ms")
115
+
116
+ del tensor, tensor2
117
+ torch.cuda.empty_cache()
118
+
119
+ return results
120
+
121
+
122
+ def compute_benchmark():
123
+ """Test raw compute performance with matrix operations."""
124
+ import torch
125
+
126
+ device = torch.device("cuda:0")
127
+ results = {"tests": []}
128
+
129
+ sizes = [1024, 2048, 4096, 8192]
130
+ print("\n⚡ Compute Benchmark (GEMM)")
131
+ print("=" * 50)
132
+
133
+ for n in sizes:
134
+ a = torch.randn(n, n, device=device, dtype=torch.float32)
135
+ b = torch.randn(n, n, device=device, dtype=torch.float32)
136
+
137
+ # Warmup
138
+ for _ in range(3):
139
+ torch.mm(a, b)
140
+ torch.cuda.synchronize()
141
+
142
+ # Benchmark
143
+ num_runs = 10
144
+ start = time.perf_counter()
145
+ for _ in range(num_runs):
146
+ torch.mm(a, b)
147
+ torch.cuda.synchronize()
148
+ elapsed = (time.perf_counter() - start) / num_runs * 1000
149
+
150
+ # TFLOPS = 2 * N^3 / time
151
+ tflops = (2 * n**3) / (elapsed / 1000) / 1e12
152
+
153
+ result = {
154
+ "matrix_size": n,
155
+ "avg_ms": round(elapsed, 2),
156
+ "tflops": round(tflops, 2),
157
+ }
158
+ results["tests"].append(result)
159
+ print(f" {n:>5}x{n} → {elapsed:8.2f} ms ({tflops:6.2f} TFLOPS)")
160
+
161
+ del a, b
162
+ torch.cuda.empty_cache()
163
+
164
+ # FP16 test
165
+ print("\n⚡ Compute Benchmark (GEMM FP16)")
166
+ print("=" * 50)
167
+ for n in [4096, 8192]:
168
+ a = torch.randn(n, n, device=device, dtype=torch.float16)
169
+ b = torch.randn(n, n, device=device, dtype=torch.float16)
170
+
171
+ for _ in range(3):
172
+ torch.mm(a, b)
173
+ torch.cuda.synchronize()
174
+
175
+ num_runs = 10
176
+ start = time.perf_counter()
177
+ for _ in range(num_runs):
178
+ torch.mm(a, b)
179
+ torch.cuda.synchronize()
180
+ elapsed = (time.perf_counter() - start) / num_runs * 1000
181
+ tflops = (2 * n**3) / (elapsed / 1000) / 1e12
182
+
183
+ result = {
184
+ "matrix_size": f"{n}_fp16",
185
+ "avg_ms": round(elapsed, 2),
186
+ "tflops": round(tflops, 2),
187
+ }
188
+ results["tests"].append(result)
189
+ print(f" {n:>5}x{n} FP16 → {elapsed:8.2f} ms ({tflops:6.2f} TFLOPS)")
190
+
191
+ del a, b
192
+ torch.cuda.empty_cache()
193
+
194
+ return results
195
+
196
+
197
+ def run_all_benchmarks():
198
+ """Run all benchmarks and save results."""
199
+ print("🔥 ROCm Forge — MI300X Benchmark Suite")
200
+ print("=" * 50)
201
+
202
+ if not check_rocm_available():
203
+ print("\n⚠️ Run this on AMD Developer Cloud with MI300X GPU.")
204
+ print(" python benchmark/rocm_benchmark.py --all")
205
+ return
206
+
207
+ results = {
208
+ "benchmark_version": "1.0",
209
+ "timestamp": datetime.now().isoformat(),
210
+ "device_info": device_info_benchmark(),
211
+ "memory": memory_benchmark(),
212
+ "compute": compute_benchmark(),
213
+ }
214
+
215
+ # Save results
216
+ os.makedirs("benchmark/results", exist_ok=True)
217
+ output_file = f"benchmark/results/benchmark_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
218
+ with open(output_file, "w") as f:
219
+ json.dump(results, f, indent=2)
220
+
221
+ print(f"\n✅ Results saved to {output_file}")
222
+ print("📸 Use these results as proof of AMD GPU usage in your hackathon submission!")
223
+
224
+
225
+ if __name__ == "__main__":
226
+ parser = argparse.ArgumentParser(description="ROCm Forge MI300X Benchmark Suite")
227
+ parser.add_argument("--all", action="store_true", help="Run all benchmarks")
228
+ parser.add_argument("--device-info", action="store_true", help="Show device information")
229
+ parser.add_argument("--memory", action="store_true", help="Run memory benchmarks")
230
+ parser.add_argument("--compute", action="store_true", help="Run compute benchmarks")
231
+ args = parser.parse_args()
232
+
233
+ if args.all:
234
+ run_all_benchmarks()
235
+ elif args.device_info:
236
+ if check_rocm_available():
237
+ device_info_benchmark()
238
+ elif args.memory:
239
+ if check_rocm_available():
240
+ memory_benchmark()
241
+ elif args.compute:
242
+ if check_rocm_available():
243
+ compute_benchmark()
244
+ else:
245
+ parser.print_help()
246
+ print("\n💡 Quick start: python benchmark/rocm_benchmark.py --all")