Skip to content

Commit 3057f77

Browse files
committed
tooling(probe): real-CCE-loss single fwd+bwd mem probe + chunk debug marker
probe_real_step_mem_20260601.py runs the production CCE loss (not the naive logits.mean() that trips the SDPA-mask VJP) for one fwd+bwd(+AdamW) step and reports MLX peak, torch-CUDA peak (where the Path-B mamba3 bwd scratch lives) and VmRSS. Adds CPPMEGA_MAMBA3_BWD_SEQ_CHUNK_DEBUG stderr marker so chunk engagement is observable.
1 parent 5fc3cf4 commit 3057f77

2 files changed

Lines changed: 139 additions & 0 deletions

File tree

cppmega_mlx/nn/_tilelang/_cuda_eager.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
# the in-tree prim builders in ``sparse_mla_path_c.py`` / ``mamba3_path_c.py``.
4444

4545
import functools
46+
import os
4647
from typing import Any
4748

4849
import mlx.core as mx
@@ -776,6 +777,16 @@ def mamba3_mimo_bwd_cuda_eager(
776777

777778
chunk = mamba3_bwd_seq_chunk()
778779
if chunk > 0 and seq > chunk:
780+
if os.environ.get("CPPMEGA_MAMBA3_BWD_SEQ_CHUNK_DEBUG", "").strip():
781+
import sys as _sys
782+
783+
print(
784+
f"[mamba3-chunk] seq={seq} chunk={chunk} "
785+
f"({(seq + chunk - 1) // chunk} chunks)",
786+
file=_sys.stderr,
787+
flush=True,
788+
)
789+
779790
def _cuda_fwd_state(x_c, B_c, C_c, z_c, A_c, dt_c, D_c, h0_c):
780791
out = mamba3_mimo_fwd_cuda_eager(
781792
x_c, B_c, C_c, z_c, A_c, dt_c, D_c, h0_c
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""Real-loss single fwd+bwd(+optimizer) memory probe for the 1B model.
2+
3+
Unlike probe_moe_forward_mem.py (naive logits.mean() loss that trips the SDPA
4+
mask VJP), this uses the production CCE training loss and the real AdamW step,
5+
and reports BOTH the MLX peak and the torch-CUDA peak (the Path-B mamba3 bwd
6+
scratch lives in the torch allocator, invisible to mx.get_peak_memory). It also
7+
samples /proc/self/status VmRSS so the true unified-memory footprint is visible.
8+
9+
Usage:
10+
python scripts/probe_real_step_mem_20260601.py --batch 1 --seq 4096 [--optimizer]
11+
"""
12+
13+
from __future__ import annotations
14+
15+
import argparse
16+
import json
17+
import os
18+
import time
19+
20+
import mlx.core as mx
21+
import mlx.nn as nn
22+
import numpy as np
23+
24+
from cppmega_mlx.recipes.model_factory import local_gb10_quarter
25+
from cppmega_mlx.training.loss import next_token_cut_cross_entropy
26+
27+
28+
def _peak_gb() -> float:
29+
fn = getattr(mx, "get_peak_memory", None)
30+
return float(fn()) / (1024**3) if fn else float("nan")
31+
32+
33+
def _reset_peak() -> None:
34+
if hasattr(mx, "reset_peak_memory"):
35+
mx.reset_peak_memory()
36+
37+
38+
def _rss_gb() -> float:
39+
try:
40+
with open("/proc/self/status") as f:
41+
for line in f:
42+
if line.startswith("VmRSS:"):
43+
return int(line.split()[1]) / (1024**2) # kB -> GB
44+
except Exception:
45+
pass
46+
return float("nan")
47+
48+
49+
def main() -> None:
50+
ap = argparse.ArgumentParser()
51+
ap.add_argument("--batch", type=int, default=1)
52+
ap.add_argument("--seq", type=int, default=4096)
53+
ap.add_argument("--vocab", type=int, default=65_536)
54+
ap.add_argument("--optimizer", action="store_true", help="also run AdamW update")
55+
args = ap.parse_args()
56+
57+
try:
58+
import torch
59+
60+
have_torch = torch.cuda.is_available()
61+
except Exception:
62+
torch = None
63+
have_torch = False
64+
65+
t0 = time.time()
66+
model = local_gb10_quarter(dtype=mx.bfloat16, grad_checkpoint=True)
67+
mx.eval(model.parameters())
68+
mx.synchronize()
69+
build_s = time.time() - t0
70+
after_params_gb = _peak_gb()
71+
72+
rng = np.random.RandomState(0)
73+
tokens = mx.array(
74+
rng.randint(0, args.vocab, size=(args.batch, args.seq + 1)).astype(np.int32)
75+
)
76+
batch = {"tokens": tokens}
77+
78+
optimizer = None
79+
if args.optimizer:
80+
from cppmega_mlx.training.optimizers import make_adamw
81+
82+
optimizer = make_adamw(learning_rate=1e-4, weight_decay=0.0)
83+
optimizer.init(model.trainable_parameters())
84+
mx.eval(model.parameters(), optimizer.state)
85+
86+
if have_torch:
87+
torch.cuda.empty_cache()
88+
torch.cuda.reset_peak_memory_stats()
89+
_reset_peak()
90+
91+
def loss_fn(m, b):
92+
return next_token_cut_cross_entropy(m, b, eval_chunks=False)
93+
94+
t1 = time.time()
95+
(loss, ntok), grads = nn.value_and_grad(model, loss_fn)(model, batch)
96+
if optimizer is not None:
97+
optimizer.update(model, grads)
98+
mx.eval(model.parameters(), optimizer.state, loss, ntok)
99+
else:
100+
mx.eval(loss, ntok, grads)
101+
mx.synchronize()
102+
run_s = time.time() - t1
103+
104+
peak_gb = _peak_gb()
105+
torch_peak_gb = (
106+
round(float(torch.cuda.max_memory_allocated()) / (1024**3), 3)
107+
if have_torch
108+
else None
109+
)
110+
result = {
111+
"efficient_moe": os.environ.get("CPPMEGA_MOE_EFFICIENT"),
112+
"mamba3_bwd_seq_chunk": os.environ.get("CPPMEGA_MAMBA3_BWD_SEQ_CHUNK") or None,
113+
"batch": args.batch,
114+
"seq": args.seq,
115+
"optimizer": bool(args.optimizer),
116+
"build_s": round(build_s, 2),
117+
"run_s": round(run_s, 2),
118+
"after_params_peak_gb": round(after_params_gb, 3),
119+
"mlx_peak_gb": round(peak_gb, 3),
120+
"torch_cuda_peak_gb": torch_peak_gb,
121+
"rss_gb": round(_rss_gb(), 3),
122+
"loss": float(loss),
123+
}
124+
print("REALPROBE_RESULT " + json.dumps(result), flush=True)
125+
126+
127+
if __name__ == "__main__":
128+
main()

0 commit comments

Comments
 (0)