Skip to content

Commit 0b48e05

Browse files
committed
tooling(moe): forward/backward peak-memory probe for 1B model at chosen batch/seq
Standalone probe building the real local_gb10_quarter model (bf16, grad-ckpt) to measure MLX peak memory for one forward (or fwd+bwd) at a given batch/seq, gated by CPPMEGA_MOE_EFFICIENT. Used to verify the efficient MoE brings seq=4096 under the OOM wall before any full train step, per strict gb10 memory discipline.
1 parent c19d4a8 commit 0b48e05

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

scripts/probe_moe_forward_mem.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
"""Forward-only peak-memory probe for the 1B local_gb10_quarter model.
3+
4+
Builds the real model (bf16, grad-checkpoint) and runs a SINGLE forward pass at
5+
the requested batch/seq, reporting MLX peak memory. Used to measure whether the
6+
env-gated efficient MoE (CPPMEGA_MOE_EFFICIENT=1) brings seq=4096 under the OOM
7+
wall before any backward/optimizer step is attempted.
8+
9+
Usage:
10+
python scripts/probe_moe_forward_mem.py --batch 1 --seq 4096 [--backward]
11+
12+
Set CPPMEGA_MOE_EFFICIENT=1 in the env to exercise the sparse-gather MoE.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import argparse
18+
import json
19+
import os
20+
import time
21+
22+
import mlx.core as mx
23+
import numpy as np
24+
25+
from cppmega_mlx.recipes.model_factory import local_gb10_quarter
26+
27+
28+
def _peak_gb() -> float:
29+
fn = getattr(mx, "get_peak_memory", None)
30+
if fn is None:
31+
metal = getattr(mx, "metal", None)
32+
fn = getattr(metal, "get_peak_memory", None) if metal else None
33+
return float(fn()) / (1024**3) if fn else float("nan")
34+
35+
36+
def _reset_peak() -> None:
37+
if hasattr(mx, "reset_peak_memory"):
38+
mx.reset_peak_memory()
39+
else:
40+
metal = getattr(mx, "metal", None)
41+
if metal and hasattr(metal, "reset_peak_memory"):
42+
metal.reset_peak_memory()
43+
44+
45+
def main() -> None:
46+
ap = argparse.ArgumentParser()
47+
ap.add_argument("--batch", type=int, default=1)
48+
ap.add_argument("--seq", type=int, default=4096)
49+
ap.add_argument("--backward", action="store_true", help="also run one backward")
50+
ap.add_argument("--vocab", type=int, default=65_536)
51+
args = ap.parse_args()
52+
53+
efficient = os.environ.get("CPPMEGA_MOE_EFFICIENT", "").strip().lower() in {
54+
"1",
55+
"true",
56+
"on",
57+
"yes",
58+
}
59+
t0 = time.time()
60+
model = local_gb10_quarter(dtype=mx.bfloat16, grad_checkpoint=True)
61+
mx.eval(model.parameters())
62+
mx.synchronize()
63+
build_s = time.time() - t0
64+
after_params_gb = _peak_gb()
65+
66+
rng = np.random.RandomState(0)
67+
ids = mx.array(rng.randint(0, args.vocab, size=(args.batch, args.seq)).astype(np.int32))
68+
69+
_reset_peak()
70+
if args.backward:
71+
import mlx.nn as nn
72+
73+
def loss_fn(m, x):
74+
out = m(x)
75+
logits = out.logits if hasattr(out, "logits") else out
76+
return logits.astype(mx.float32).mean()
77+
78+
lg = nn.value_and_grad(model, loss_fn)
79+
t1 = time.time()
80+
l, g = lg(model, ids)
81+
mx.eval(l, g)
82+
mx.synchronize()
83+
run_s = time.time() - t1
84+
loss_val = float(l)
85+
else:
86+
t1 = time.time()
87+
out = model(ids)
88+
logits = out.logits if hasattr(out, "logits") else out
89+
mx.eval(logits)
90+
mx.synchronize()
91+
run_s = time.time() - t1
92+
loss_val = float(logits.astype(mx.float32).mean())
93+
94+
peak_gb = _peak_gb()
95+
result = {
96+
"efficient_moe": efficient,
97+
"batch": args.batch,
98+
"seq": args.seq,
99+
"backward": bool(args.backward),
100+
"build_s": round(build_s, 2),
101+
"run_s": round(run_s, 2),
102+
"after_params_peak_gb": round(after_params_gb, 3),
103+
"peak_gb": round(peak_gb, 3),
104+
"loss_or_mean": loss_val,
105+
}
106+
print("PROBE_RESULT " + json.dumps(result), flush=True)
107+
108+
109+
if __name__ == "__main__":
110+
main()

0 commit comments

Comments
 (0)