| |
| """ |
| eval_dummy.py β Evaluate Tiered vs Baseline HRM using dummy (random) tensors. |
| |
| No datasets or checkpoints needed. This measures pure model throughput, |
| latency, and memory usage on synthetic inputs. |
| |
| Usage: |
| source venv/bin/activate |
| python eval_dummy.py |
| python eval_dummy.py --batch-size 64 --seq-len 256 --hidden-size 1024 |
| python eval_dummy.py --iterations 50 --plot |
| """ |
|
|
| import argparse |
| import json |
| import os |
| import sys |
| import time |
| from dataclasses import dataclass, asdict |
|
|
| import torch |
| import torch.nn.functional as F |
|
|
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
|
|
| from models.memory_tier import MemoryTierManager |
| from models.hrm.hrm_tiered import HRM_Tiered |
| from models.hrm.hrm_act_v1 import HierarchicalReasoningModel_ACTV1 |
|
|
|
|
| |
| |
| |
|
|
| def make_batch(batch_size, seq_len, vocab_size, device): |
| return { |
| "inputs": torch.randint(0, vocab_size, (batch_size, seq_len), device=device), |
| "labels": torch.randint(0, vocab_size, (batch_size, seq_len), device=device), |
| "puzzle_identifiers": torch.arange(batch_size, device=device), |
| } |
|
|
|
|
| |
| |
| |
|
|
| @dataclass |
| class EvalResult: |
| model_name: str |
| batch_size: int |
| seq_len: int |
| hidden_size: int |
| param_count: int |
| latency_mean_ms: float |
| latency_std_ms: float |
| throughput_sps: float |
| gpu_memory_mb: float |
|
|
|
|
| def eval_model(model, model_name, batch, device, warmup=5, iterations=20): |
| """Run inference on dummy data and collect timing stats.""" |
| model.eval() |
| bs = batch["inputs"].shape[0] |
| sl = batch["inputs"].shape[1] |
| param_count = sum(p.numel() for p in model.parameters()) |
|
|
| |
| with torch.no_grad(): |
| for _ in range(warmup): |
| carry = model.initial_carry(batch) |
| carry.inner_carry.z_H = carry.inner_carry.z_H.to(device) |
| carry.inner_carry.z_L = carry.inner_carry.z_L.to(device) |
| carry.steps = carry.steps.to(device) |
| carry.halted = carry.halted.to(device) |
| carry.current_data = {k: v.to(device) for k, v in carry.current_data.items()} |
| model(carry, batch) |
|
|
| if torch.cuda.is_available(): |
| torch.cuda.reset_peak_memory_stats(device) |
| torch.cuda.synchronize() |
|
|
| |
| latencies = [] |
| with torch.no_grad(): |
| for _ in range(iterations): |
| carry = model.initial_carry(batch) |
| carry.inner_carry.z_H = carry.inner_carry.z_H.to(device) |
| carry.inner_carry.z_L = carry.inner_carry.z_L.to(device) |
| carry.steps = carry.steps.to(device) |
| carry.halted = carry.halted.to(device) |
| carry.current_data = {k: v.to(device) for k, v in carry.current_data.items()} |
|
|
| if torch.cuda.is_available(): |
| start = torch.cuda.Event(enable_timing=True) |
| end = torch.cuda.Event(enable_timing=True) |
| start.record() |
|
|
| model(carry, batch) |
|
|
| if torch.cuda.is_available(): |
| end.record() |
| torch.cuda.synchronize() |
| latencies.append(start.elapsed_time(end)) |
| else: |
| pass |
|
|
| mean_ms = sum(latencies) / len(latencies) |
| std_ms = (sum((x - mean_ms)**2 for x in latencies) / max(len(latencies)-1, 1)) ** 0.5 |
| throughput = bs / (mean_ms / 1000) if mean_ms > 0 else 0 |
| gpu_mem = torch.cuda.max_memory_allocated(device) / (1024**2) if torch.cuda.is_available() else 0 |
|
|
| return EvalResult( |
| model_name=model_name, |
| batch_size=bs, seq_len=sl, |
| hidden_size=model.config.hidden_size, |
| param_count=param_count, |
| latency_mean_ms=mean_ms, |
| latency_std_ms=std_ms, |
| throughput_sps=throughput, |
| gpu_memory_mb=gpu_mem, |
| ) |
|
|
|
|
| |
| |
| |
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="HRM Dummy Tensor Evaluation") |
| parser.add_argument("--batch-size", type=int, default=8) |
| parser.add_argument("--seq-len", type=int, default=81, help="Sudoku=81, or any length") |
| parser.add_argument("--hidden-size", type=int, default=512) |
| parser.add_argument("--num-heads", type=int, default=8) |
| parser.add_argument("--H-cycles", type=int, default=2) |
| parser.add_argument("--L-cycles", type=int, default=2) |
| parser.add_argument("--H-layers", type=int, default=4) |
| parser.add_argument("--L-layers", type=int, default=4) |
| parser.add_argument("--warmup", type=int, default=5) |
| parser.add_argument("--iterations", type=int, default=20) |
| parser.add_argument("--output", type=str, default="benchmark_results/eval_dummy.json") |
| parser.add_argument("--plot", action="store_true") |
| args = parser.parse_args() |
|
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| vocab_size = 32 |
|
|
| config_dict = { |
| "batch_size": args.batch_size, |
| "seq_len": args.seq_len, |
| "puzzle_emb_ndim": 0, |
| "num_puzzle_identifiers": args.batch_size, |
| "vocab_size": vocab_size, |
| "H_cycles": args.H_cycles, |
| "L_cycles": args.L_cycles, |
| "H_layers": args.H_layers, |
| "L_layers": args.L_layers, |
| "hidden_size": args.hidden_size, |
| "expansion": 4.0, |
| "num_heads": args.num_heads, |
| "pos_encodings": "rope", |
| "halt_max_steps": 1, |
| "halt_exploration_prob": 0.0, |
| } |
|
|
| batch = make_batch(args.batch_size, args.seq_len, vocab_size, device) |
|
|
| print(f"\n{'='*64}") |
| print(f" HRM Dummy Tensor Evaluation") |
| print(f" Device: {device} ({torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'})") |
| print(f" Batch: {args.batch_size}") |
| print(f" Seq Len: {args.seq_len}") |
| print(f" Hidden: {args.hidden_size}") |
| print(f" H/L cycles: {args.H_cycles}/{args.L_cycles}") |
| print(f" H/L layers: {args.H_layers}/{args.L_layers}") |
| print(f" Iterations: {args.iterations} (warmup: {args.warmup})") |
| print(f"{'='*64}") |
|
|
| results = [] |
|
|
| |
| print(f"\n β Evaluating Baseline (hrm_act_v1)...") |
| baseline_model = HierarchicalReasoningModel_ACTV1(config_dict).to(device) |
| r_baseline = eval_model(baseline_model, "Baseline", batch, device, args.warmup, args.iterations) |
| results.append(r_baseline) |
| del baseline_model |
| torch.cuda.empty_cache() |
|
|
| |
| print(f" β Evaluating Tiered (SRAM/DRAM Triton)...") |
| mem_mgr = MemoryTierManager(device=device, enable_tracking=True) |
| tiered_model = HRM_Tiered(config_dict, memory_manager=mem_mgr).to(device) |
| r_tiered = eval_model(tiered_model, "Tiered", batch, device, args.warmup, args.iterations) |
|
|
| |
| tier_timing = tiered_model.get_timing_stats() |
| tier_mem = mem_mgr.get_stats() |
| results.append(r_tiered) |
| del tiered_model |
| torch.cuda.empty_cache() |
|
|
| |
| print(f"\n{'='*64}") |
| print(f" {'Model':<12} {'Params':>10} {'Latency(ms)':>13} {'Β±Ο':>8} {'Throughput':>12} {'GPU MB':>8}") |
| print(f" {'-'*58}") |
| for r in results: |
| print(f" {r.model_name:<12} {r.param_count/1e6:>9.1f}M {r.latency_mean_ms:>13.2f} {r.latency_std_ms:>8.2f} {r.throughput_sps:>12.1f} {r.gpu_memory_mb:>8.1f}") |
|
|
| |
| speedup = r_baseline.latency_mean_ms / r_tiered.latency_mean_ms if r_tiered.latency_mean_ms > 0 else 0 |
| mem_diff = r_baseline.gpu_memory_mb - r_tiered.gpu_memory_mb |
| print(f"\n Speedup: {speedup:.2f}x") |
| print(f" Memory saving: {mem_diff:.1f} MB") |
|
|
| |
| if tier_timing: |
| l_us = tier_timing.get("L_forward_us", {}).get("mean_us", 0) |
| h_us = tier_timing.get("H_forward_us", {}).get("mean_us", 0) |
| ratio = h_us / l_us if l_us > 0 else 0 |
| print(f"\n L-level (SRAM): {l_us:.1f} ΞΌs") |
| print(f" H-level (DRAM): {h_us:.1f} ΞΌs") |
| print(f" H/L ratio: {ratio:.2f}x") |
|
|
| if tier_mem: |
| print(f" SRAM hit rate: {tier_mem['sram']['hit_rate']:.2%}") |
| print(f"{'='*64}\n") |
|
|
| |
| os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True) |
| with open(args.output, "w") as f: |
| json.dump([asdict(r) for r in results], f, indent=2) |
| print(f" Results saved β {args.output}") |
|
|
| |
| if args.plot: |
| try: |
| import matplotlib |
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
|
|
| fig, axes = plt.subplots(1, 3, figsize=(15, 5)) |
| fig.suptitle("HRM Dummy Eval: Baseline vs Tiered", fontweight="bold") |
| names = [r.model_name for r in results] |
| colors = ["#e74c3c", "#2ecc71"] |
|
|
| |
| axes[0].bar(names, [r.latency_mean_ms for r in results], color=colors) |
| axes[0].set_ylabel("Latency (ms)") |
| axes[0].set_title("Inference Latency") |
| axes[0].grid(axis="y", alpha=0.3) |
|
|
| |
| axes[1].bar(names, [r.throughput_sps for r in results], color=colors) |
| axes[1].set_ylabel("Samples/sec") |
| axes[1].set_title("Throughput") |
| axes[1].grid(axis="y", alpha=0.3) |
|
|
| |
| axes[2].bar(names, [r.gpu_memory_mb for r in results], color=colors) |
| axes[2].set_ylabel("GPU Memory (MB)") |
| axes[2].set_title("Peak Memory") |
| axes[2].grid(axis="y", alpha=0.3) |
|
|
| plt.tight_layout() |
| plot_dir = os.path.dirname(args.output) or "benchmark_results" |
| plot_path = os.path.join(plot_dir, "eval_dummy_comparison.png") |
| plt.savefig(plot_path, dpi=150) |
| plt.close() |
| print(f" Plot saved β {plot_path}") |
| except ImportError: |
| print(" (matplotlib not found β skipping plots)") |
|
|
| print(" Done!\n") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|