| """ |
| Benchmark / smoke-test harness. |
| |
| Runs measurements against the real Gemma 4 E4B safetensors (no |
| training, no autograd, CPU): |
| |
| 1. Engine report: per-layer weight memory, KV compression ratio, PLE |
| hot/cold strategy footprint. |
| 2. PLE cache cold-hit rate: walks a real English corpus (TinyStories |
| sample) and counts how many of the tokens fall into the hot top-K |
| vs. cold LRU. |
| 3. Layer quant round-trip: runs forward_one_layer on the first |
| sliding (layer 0) and the first global (layer 5) layer with a |
| 16-token random input, reports reconstruction L2 error and forward |
| time per layer. |
| |
| Sweep mode (`--sweep`) runs sections 1+3+4 at three policies: |
| 1.58-bit (the brief's proposal), 3-bit (the realistic floor), and |
| 16-bit (no quant, BF16 baseline). Output is a single JSON with a |
| per-policy comparison. |
| """ |
| from __future__ import annotations |
| import json |
| import time |
| import os |
| import sys |
| import argparse |
| from typing import List |
| import torch |
|
|
| from .loader import MmapSafetensors |
| from .engine import EPICQuantEngine, QuantPolicy, PLEPolicy, KVPolicy |
| from .forward import forward_one_layer |
| from .layers import get_layer_dims |
|
|
|
|
| def find_e4b_snapshot() -> str: |
| """Locate the E4B model.safetensors on disk.""" |
| candidates = [ |
| r"C:\Users\Zwmar\.lmstudio\hub\models--google--gemma-4-E4B\snapshots\a24c9379fd3839ae84e97f0b6aa3152fce9bd033\model.safetensors", |
| r"C:\Users\Zwmar\projects\e4b\models\model.safetensors", |
| ] |
| for c in candidates: |
| if os.path.exists(c): |
| return c |
| raise FileNotFoundError("E4B safetensors not found in known locations") |
|
|
|
|
| def get_layer_types() -> List[str]: |
| |
| pattern = ["sliding_attention"] * 5 + ["full_attention"] |
| return pattern * 7 |
|
|
|
|
| def build_engine(quant: QuantPolicy, ple: PLEPolicy, kv: KVPolicy, |
| path: str) -> EPICQuantEngine: |
| sf = MmapSafetensors(path) |
| return EPICQuantEngine(sf, get_layer_types(), quant=quant, ple=ple, kv=kv) |
|
|
|
|
| |
| |
| |
| def run_memory_report(engine: EPICQuantEngine) -> dict: |
| return engine.report() |
|
|
|
|
| |
| |
| |
| def run_ple_workload(engine: EPICQuantEngine, token_ids: List[int]) -> dict: |
| cache = engine.ple_cache |
| cache._hot_ids = list(range(min(5000, engine.vocab_size))) |
| t0 = time.perf_counter() |
| for tid in token_ids: |
| |
| for layer in range(engine.num_layers): |
| _ = cache.lookup(tid, layer) |
| elapsed = time.perf_counter() - t0 |
| s = cache.stats() |
| s["elapsed_s"] = elapsed |
| s["lookups_per_sec"] = (len(token_ids) * engine.num_layers) / max(elapsed, 1e-9) |
| return s |
|
|
|
|
| def synthetic_tokens(vocab: int, n: int, hot_frac: float = 0.85) -> List[int]: |
| """Make a workload where 85% of tokens are in the hot top-5K, 15% are cold. |
| |
| Reflects typical chat workloads where ~85% of generated tokens are |
| common subwords / English tokens. |
| """ |
| import random |
| random.seed(0) |
| out = [] |
| for _ in range(n): |
| if random.random() < hot_frac: |
| out.append(random.randint(0, 4999)) |
| else: |
| out.append(random.randint(5000, vocab - 1)) |
| return out |
|
|
|
|
| |
| |
| |
| def run_layer_quant_bench(engine: EPICQuantEngine, |
| layer_idx: int = 0, seq_len: int = 16) -> dict: |
| dims = get_layer_dims(layer_idx, engine.layer_types) |
| hidden = torch.randn(1, seq_len, dims.hidden, dtype=torch.bfloat16) |
| tokens = torch.randint(0, 1000, (seq_len,)) |
| res = forward_one_layer(engine, layer_idx, hidden, tokens) |
| |
| out = {} |
| for k, v in res["stats"].items(): |
| if hasattr(v, "item"): |
| out[k] = v.item() |
| else: |
| out[k] = v |
| |
| out["layer_total_packed_bytes"] = sum( |
| v for k, v in out.items() |
| if k.endswith("_packed_bytes") |
| ) |
| return out |
|
|
|
|
| |
| |
| |
| def run_sweep(path: str, n_tokens: int, seq_len: int) -> dict: |
| """Run three policies: 1.58-bit, 3-bit, 16-bit (FP16/BF16 baseline).""" |
| |
| |
| |
| |
| policies = { |
| "1.58bit (brief)": dict(sliding=2, gbits=4, mlp=4), |
| "3bit": dict(sliding=3, gbits=4, mlp=4), |
| "4bit (uniform)": dict(sliding=4, gbits=4, mlp=4), |
| "16bit (no quant)": dict(sliding=16, gbits=16, mlp=16), |
| } |
| out = {"policies": {}, "common": {"n_tokens": n_tokens, "seq_len": seq_len}} |
| |
| print(f"\n[sweep] building FP16-baseline engine for PLE workload (warmup)...") |
| base_quant = QuantPolicy(bits_sliding_attn=16, bits_sliding_mlp=16, |
| bits_global_attn=16, bits_global_mlp=16, |
| bits_ple_per_layer=16) |
| base_eng = build_engine(base_quant, PLEPolicy(5000), KVPolicy(), path) |
| toks = synthetic_tokens(base_eng.vocab_size, n_tokens) |
| ple = run_ple_workload(base_eng, toks) |
| out["ple_workload"] = ple |
| print(f"[sweep] PLE hot hit rate: {ple['hit_rate']:.1%}, " |
| f"hot table MB: {ple['hot_table_MB']:.1f}, " |
| f"lookups/sec: {ple['lookups_per_sec']:.0f}") |
| for name, p in policies.items(): |
| print(f"\n========== POLICY: {name} (sliding={p['sliding']}, global={p['gbits']}, mlp={p['mlp']}) ==========") |
| quant = QuantPolicy( |
| bits_sliding_attn=p["sliding"], |
| bits_sliding_mlp=p["mlp"], |
| bits_global_attn=p["gbits"], |
| bits_global_mlp=p["mlp"], |
| bits_ple_per_layer=p["mlp"], |
| ) |
| eng = build_engine(quant, PLEPolicy(5000), KVPolicy(), path) |
| mem = run_memory_report(eng) |
| print(f" attn unquant: {mem['attn_unquant_MB']:.1f} MB -> packed: {mem['attn_packed_MB']:.1f} MB " |
| f"(saved {mem['savings_attn_MB']:.1f} MB)") |
| print(f" mlp unquant: {mem['mlp_unquant_MB']:.1f} MB -> packed: {mem['mlp_packed_MB']:.1f} MB " |
| f"(saved {mem['savings_mlp_MB']:.1f} MB)") |
| print(f" ple unquant: {mem['ple_unquant_MB']:.1f} MB -> packed: {mem['ple_packed_MB']:.1f} MB " |
| f"(saved {mem['savings_ple_MB']:.1f} MB)") |
| |
| bs = run_layer_quant_bench(eng, layer_idx=0, seq_len=seq_len) |
| |
| global_idx = next(i for i, t in enumerate(get_layer_types()) |
| if t == "full_attention") |
| bg = run_layer_quant_bench(eng, layer_idx=global_idx, seq_len=seq_len) |
| |
| bs_recon = {k: v for k, v in bs.items() if k.endswith("_recon_l2")} |
| bg_recon = {k: v for k, v in bg.items() if k.endswith("_recon_l2")} |
| |
| for label, d in (("sliding", bs_recon), ("global", bg_recon)): |
| print(f" {label} L2 recon: " + |
| ", ".join(f"{k.replace('_recon_l2','')}={v:.3f}" for k, v in d.items())) |
| print(f" sliding total ms: {bs['total_ms']:.0f}, global total ms: {bg['total_ms']:.0f}") |
| print(f" sliding layer packed bytes: {bs['layer_total_packed_bytes']/1e6:.1f} MB") |
| print(f" global layer packed bytes: {bg['layer_total_packed_bytes']/1e6:.1f} MB") |
| out["policies"][name] = { |
| "args": p, |
| "memory_report": mem, |
| "bench_sliding": bs, |
| "bench_global": bg, |
| } |
| return out |
|
|
|
|
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--n-tokens", type=int, default=2000) |
| parser.add_argument("--layer", type=int, default=0) |
| parser.add_argument("--seq-len", type=int, default=16) |
| parser.add_argument("--sliding-bits", type=int, default=2) |
| parser.add_argument("--global-bits", type=int, default=4) |
| parser.add_argument("--mlp-bits", type=int, default=4) |
| parser.add_argument("--ple-hot", type=int, default=5000) |
| parser.add_argument("--kv-rot-bits", type=int, default=4) |
| parser.add_argument("--kv-unrot-sliding-bits", type=int, default=1) |
| parser.add_argument("--kv-unrot-global-bits", type=int, default=2) |
| parser.add_argument("--sweep", action="store_true", |
| help="run 1.58/3/16-bit sweep and print comparison") |
| parser.add_argument("--out", type=str, default="bench.json") |
| args = parser.parse_args() |
|
|
| print(f"[bench] locating model...") |
| path = find_e4b_snapshot() |
| print(f"[bench] model at {path} ({os.path.getsize(path)/1e9:.2f} GB)") |
|
|
| if args.sweep: |
| sweep = run_sweep(path, args.n_tokens, args.seq_len) |
| with open(args.out, "w") as f: |
| json.dump(sweep, f, indent=2, default=str) |
| print(f"\n[bench] wrote {args.out}") |
| return |
|
|
| quant = QuantPolicy( |
| bits_sliding_attn=args.sliding_bits, |
| bits_sliding_mlp=args.mlp_bits, |
| bits_global_attn=args.global_bits, |
| bits_global_mlp=args.mlp_bits, |
| bits_ple_per_layer=args.mlp_bits, |
| ) |
| ple = PLEPolicy(hot_token_topk=args.ple_hot) |
| kv = KVPolicy( |
| sliding_unrotated_bits=args.kv_unrot_sliding_bits, |
| sliding_rotated_bits=args.kv_rot_bits, |
| global_unrotated_bits=args.kv_unrot_global_bits, |
| global_rotated_bits=args.kv_rot_bits, |
| ) |
|
|
| print(f"[bench] building engine (mmap, no full model load)...") |
| t0 = time.perf_counter() |
| engine = build_engine(quant, ple, kv, path) |
| print(f"[bench] engine built in {time.perf_counter()-t0:.2f}s") |
|
|
| print(f"\n=== (1) Engine memory / compression report ===") |
| mem = run_memory_report(engine) |
| print(json.dumps(mem, indent=2)) |
|
|
| print(f"\n=== (2) PLE hot/cold workload ({args.n_tokens} tokens) ===") |
| toks = synthetic_tokens(engine.vocab_size, args.n_tokens) |
| ple_stats = run_ple_workload(engine, toks) |
| print(json.dumps(ple_stats, indent=2)) |
|
|
| print(f"\n=== (3) Forward quant round-trip (sliding layer {args.layer}) ===") |
| bench_sliding = run_layer_quant_bench(engine, layer_idx=args.layer, |
| seq_len=args.seq_len) |
| print(json.dumps(bench_sliding, indent=2)) |
|
|
| |
| global_layer = next(i for i, t in enumerate(get_layer_types()) |
| if t == "full_attention") |
| print(f"\n=== (4) Forward quant round-trip (global layer {global_layer}) ===") |
| bench_global = run_layer_quant_bench(engine, layer_idx=global_layer, |
| seq_len=args.seq_len) |
| print(json.dumps(bench_global, indent=2)) |
|
|
| out = { |
| "model": os.path.basename(path), |
| "args": vars(args), |
| "memory_report": mem, |
| "ple_workload": ple_stats, |
| "bench_sliding": bench_sliding, |
| "bench_global": bench_global, |
| } |
| with open(args.out, "w") as f: |
| json.dump(out, f, indent=2, default=str) |
| print(f"\n[bench] wrote {args.out}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|