Implement Static Profiler inside post.py and add termspec comparison pipeline

This commit is contained in:
ignis 2026-05-30 09:59:29 +00:00
parent 13a6ef823d
commit 71c53c1b09
2 changed files with 223 additions and 0 deletions

View file

@ -193,9 +193,62 @@ class SympyOptimizer:
self.sympy_cache[name] = expanded_expr
return expanded_expr
def calculate_flops_and_heavy(self, expr):
flops = 0
heavy = 0
for node in sympy.preorder_traversal(expr):
if isinstance(node, sympy.Add):
flops += len(node.args) - 1
elif isinstance(node, sympy.Mul):
flops += len(node.args) - 1
elif isinstance(node, sympy.Pow):
base, exp = node.args
if exp == 0.5 or exp == -0.5:
flops += 10
heavy += 1
elif exp == -1:
flops += 4
heavy += 1
elif isinstance(exp, sympy.Integer):
val = abs(int(exp))
if val > 1:
flops += val - 1
else:
flops += 10
heavy += 1
elif isinstance(node, (sympy.Derivative, sympy.Function)):
name = node.func.__name__
if name == 'sqrt':
flops += 10
heavy += 1
elif name in ('exp', 'log', 'sin', 'cos', 'tan', 'rxn_rate'):
flops += 10
heavy += 1
elif name == 'Abs':
flops += 1
else:
flops += 10
heavy += 1
return flops, heavy
def count_3d_loads(self, expr, three_d_arrays):
count = 0
for node in sympy.preorder_traversal(expr):
if isinstance(node, sympy.Symbol) and node.name in three_d_arrays:
count += 1
return count
def optimize_field(self, name, alloc=None):
expr = self.get_sympy_expr(name)
three_d_arrays = {
k for k, v in self.fdict.items()
if hasattr(v, 'dim') and v.dim == ':,:,:'
}
before_flops, before_heavy = self.calculate_flops_and_heavy(expr)
before_loads = self.count_3d_loads(expr, three_d_arrays)
simplified_expr = sympy.simplify(expr)
simplified_expr = sympy.cancel(simplified_expr)
@ -213,6 +266,47 @@ class SympyOptimizer:
replacements, reduced_exprs = sympy.cse(simplified_expr)
reduced_expr = reduced_exprs[0]
after_flops = 0
after_heavy = 0
after_loads = 0
for temp_var, temp_expr in replacements:
f_val, h_val = self.calculate_flops_and_heavy(temp_expr)
after_flops += f_val
after_heavy += h_val
after_loads += self.count_3d_loads(temp_expr, three_d_arrays)
f_val, h_val = self.calculate_flops_and_heavy(reduced_expr)
after_flops += f_val
after_heavy += h_val
after_loads += self.count_3d_loads(reduced_expr, three_d_arrays)
import sys
def pct_str(before, after):
if before == 0:
return "0.0%" if after == 0 else "+inf%"
diff = after - before
pct = (diff / before) * 100
return f"{pct:+.1f}%"
flops_pct = pct_str(before_flops, after_flops)
heavy_pct = pct_str(before_heavy, after_heavy)
loads_pct = pct_str(before_loads, after_loads)
if after_flops < before_flops * 0.5 or after_loads < before_loads * 0.5:
est_speedup = "Highly significant"
elif after_flops < before_flops or after_loads < before_loads:
est_speedup = "Moderate"
else:
est_speedup = "Minimal / Already optimal"
sys.stderr.write(f"\n[SymPy Optimizer Report: {name}]\n")
sys.stderr.write(f"- Floating Point Ops : {before_flops} -> {after_flops} ({flops_pct})\n")
sys.stderr.write(f"- Heavy Ops (Div/Sqrt): {before_heavy} -> {after_heavy} ({heavy_pct})\n")
sys.stderr.write(f"- 3D Array Mem Reads : {before_loads} -> {after_loads} ({loads_pct})\n")
sys.stderr.write(f"=> Estimated Speedup in loop: {est_speedup}\n\n")
cse_decls = []
cse_assigns = []

View file

@ -0,0 +1,129 @@
import os
import subprocess
import difflib
import re
termspecs = [
"termspec.20210708",
"termspec.banded_fsd-0.05-0.95-0.1",
"termspec.fsd-pos-neg",
"termspec.masked_fsd-0.1-0.9",
"termspec.pdf-sampling-ic1",
"termspec.pdf-sampling-ic1-ddxc",
"termspec.t1-8",
"termspec.vnsd_fluctuation"
]
workspace_dir = "/home/ignis/workspace/incomp-flame-post"
termspecs_dir = os.path.join(workspace_dir, "code/termspecs")
artifacts_dir = "/home/ignis/.gemini/antigravity-ide/brain/738c7ef6-4504-4268-bb13-023dffb2a2eb"
report_path = os.path.join(artifacts_dir, "sympy_comparison_report.md")
def run_cmd(args):
res = subprocess.run(args, capture_output=True, text=True, cwd=workspace_dir)
return res.stdout, res.stderr, res.returncode
def extract_loops_diff(base_code, opt_code):
"""
Diff the two source codes and extract the main loop calculation hunks.
"""
base_lines = base_code.splitlines(keepends=True)
opt_lines = opt_code.splitlines(keepends=True)
diff = list(difflib.unified_diff(base_lines, opt_lines, fromfile='baseline', tofile='optimized', n=4))
# Filter hunks to keep the report clean and concise
hunks = []
current_hunk = []
in_hunk = False
for line in diff:
if line.startswith('@@'):
if current_hunk:
hunks.append("".join(current_hunk))
current_hunk = []
in_hunk = True
current_hunk.append(line)
elif in_hunk:
current_hunk.append(line)
if current_hunk:
hunks.append("".join(current_hunk))
# Standardize or filter non-essential changes (like comments with date or file paths)
meaningful_hunks = []
for hunk in hunks:
lines = hunk.splitlines()
has_logic_change = False
for l in lines:
if (l.startswith('+') or l.startswith('-')) and not l.startswith('+++') and not l.startswith('---'):
# Ignore trivial comments or compilation timestamp lines if any
if "build date" in l or "build base" in l or "build_date" in l:
continue
has_logic_change = True
break
if has_logic_change:
meaningful_hunks.append(hunk)
return "\n".join(meaningful_hunks[:4]) # limit to first 4 meaningful hunks to keep it extremely readable
report_content = []
report_content.append("# SymPy 기호 최적화 도입 전후 생성 코드 정밀 비교 보고서\n")
report_content.append("본 보고서는 8가지 모든 termspec 설정에 대해 SymPy 대수 단순화 및 루프 단위 공통 하위식 제거(CSE) 최적화 도입 전(Baseline)과 도입 후(Optimized) 생성되는 포트란 코드를 대조 분석하여 정량적 최적화 메트릭 및 코드 수준의 차이점을 시각적으로 증명합니다.\n")
report_content.append("````carousel")
for idx, ts in enumerate(termspecs):
ts_path = os.path.join(termspecs_dir, ts)
print(f"Processing termspec: {ts}")
# Generate baseline
base_out, base_err, base_code = run_cmd([
"python3", "code/code_gen/code_gen_baseline.py", ts_path
])
# Generate optimized (profile report prints to stderr)
opt_out, opt_err, opt_code = run_cmd([
"python3", "code/code_gen/code_gen.py", ts_path
])
# Parse profile report from opt_err
reports = []
# Profile reports are bounded by [SymPy Optimizer Report: ...] and Estimated Speedup
matches = re.findall(r"(\[SymPy Optimizer Report: [a-zA-Z0-9_]+\]\n.*?\n=> Estimated Speedup in loop:.*?\n)", opt_err, re.DOTALL)
# Create diff
loop_diff = extract_loops_diff(base_out, opt_out)
report_content.append(f"## {idx+1}. {ts}\n")
report_content.append("### 📊 SymPy 정적 분석 최적화 메트릭 요약\n")
if matches:
report_content.append("```text")
for r in matches[:5]: # show up to 5 field reports for conciseness
report_content.append(r.strip())
if len(matches) > 5:
report_content.append(f"... (외 {len(matches)-5}개 필드는 생략됨)")
report_content.append("```\n")
else:
report_content.append("> [!NOTE]\n> 본 termspec은 단순 변수나 primary field 위주로 구성되어 별도의 복잡한 기호 최적화 타겟이 발견되지 않았습니다.\n")
report_content.append("### 💻 생성 코드 차이점 상세 (Diff)\n")
if loop_diff:
report_content.append("```diff")
report_content.append(loop_diff)
report_content.append("```\n")
else:
report_content.append("> [!TIP]\n> 생성된 포트란 코드 로직이 완전히 완벽하게 동일하여 물리 수치 및 연산량이 기존과 100% 일치합니다. (최적화 오버헤드 0%)\n")
if idx < len(termspecs) - 1:
report_content.append("<!-- slide -->")
report_content.append("````\n")
# Write report
os.makedirs(os.path.dirname(report_path), exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
f.write("\n".join(report_content))
print(f"SymPy termspec comparison report built at: {report_path}")