Skip to content

Commit 65b5572

Browse files
Add single-fused probe to classify the Metal two-phase instability
fused_specdecode_generate_mlx_trim(single_fused=True): skip the two-phase eval so drafter+26B fuse into ONE graph (the b876-pathological path); report per-block eval times (first8/max/mean). Harness --single-fused + preset k3-fused-singlefused-probe (n=2,gen=16 so a pathological block is bounded). Classifies fundamental command-buffer limit (eval scales w/ graph) vs fixable SDPA fallback (eval huge even at small scale). Co-authored-by: FluffyAIcode <FluffyAIcode@users.noreply.github.com>
1 parent bd3a1f9 commit 65b5572

3 files changed

Lines changed: 55 additions & 4 deletions

File tree

inference_engine/backends/mlx/fused_specdecode.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ def fused_specdecode_generate_mlx_trim(
373373
gen_tokens: int,
374374
block_size: int,
375375
eos_ids: Sequence[int] = (),
376+
single_fused: bool = False,
376377
) -> Dict[str, Any]:
377378
"""CUDA-parity fused spec decode: KEEP accepted K/V, TRIM only the rejected
378379
tail (no rollback, no carry re-forward). Requires the adapter to be
@@ -397,6 +398,7 @@ def fused_specdecode_generate_mlx_trim(
397398

398399
generated: List[int] = []
399400
accepts: List[int] = []
401+
block_evals: List[float] = []
400402
ctx_len = C
401403
try:
402404
while len(generated) < gen_tokens:
@@ -410,7 +412,11 @@ def fused_specdecode_generate_mlx_trim(
410412
ctx_kv, bonus_id, embed_fn, lm_head_fn,
411413
n_masks=n_draft, context_len=base)
412414
check_ids = mx.concatenate([bonus_id[None], drafts]) # [L]
413-
mx.eval(check_ids) # two-phase (drafter graph before 26B graph)
415+
if not single_fused:
416+
mx.eval(check_ids) # two-phase (drafter graph before 26B)
417+
# single_fused=True → leave check_ids LAZY so the drafter and
418+
# 26B verify fuse into ONE graph (the path b876 found Metal-
419+
# pathological); this probe times it to classify the instability.
414420
else:
415421
check_ids = bonus_id[None]
416422
block_logits = adapter.forward_block_lazy(check_ids[None]) # [L, V]
@@ -426,7 +432,9 @@ def fused_specdecode_generate_mlx_trim(
426432
timing["build_s"] += time.perf_counter() - t_build
427433
t_eval = time.perf_counter()
428434
mx.eval(accepted_mx, check_ids)
429-
timing["eval_s"] += time.perf_counter() - t_eval
435+
blk_eval = time.perf_counter() - t_eval
436+
timing["eval_s"] += blk_eval
437+
block_evals.append(round(blk_eval, 4))
430438
accepted = int(accepted_mx.item())
431439
check = [int(x) for x in check_ids.tolist()]
432440
commit = check[:accepted]
@@ -461,7 +469,13 @@ def fused_specdecode_generate_mlx_trim(
461469
"mean_accept_len": (round(sum(accepts) / len(accepts), 3)
462470
if accepts else 0.0),
463471
"decode_tokens": len(generated),
464-
"loop": "mlx_trim_keep_accepted_cuda_parity",
472+
"loop": ("mlx_trim_single_fused_probe" if single_fused
473+
else "mlx_trim_keep_accepted_cuda_parity"),
474+
"single_fused": bool(single_fused),
475+
"block_eval_s_first8": block_evals[:8],
476+
"block_eval_s_max": (round(max(block_evals), 4) if block_evals else None),
477+
"block_eval_s_mean": (round(sum(block_evals) / len(block_evals), 4)
478+
if block_evals else None),
465479
"time_breakdown_s": {k: round(v, 3) for k, v in timing.items()},
466480
}
467481

inference_engine/bridge/manifest.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,37 @@ def _harness_preset(
235235
timeout_minutes=45,
236236
params={"path": ("path:tests", None)},
237237
),
238+
Preset(
239+
name="k3-fused-singlefused-probe",
240+
description="PROBE: single-fused (one drafter+26B graph) vs two-phase, "
241+
"to classify the Metal instability. Small (n=2, gen=16) so a "
242+
"pathological per-block eval is bounded. Compare block_eval_s "
243+
"vs k3-fused-allmlx-code-trim (two-phase).",
244+
command_templates=(
245+
(
246+
"python3", "scripts/research/k3_integrated_niah_eval_mac.py",
247+
"--verifier-path", "${ENV:KAKEYA_MAC_VERIFIER_PATH}",
248+
"--drafter-id", "${ENV:KAKEYA_MAC_DRAFTER_ID}",
249+
"--f-theta-dir", "${ENV:KAKEYA_MAC_FTHETA_DIR}",
250+
"--s5-exact-full-attn", "--fused-specdecode",
251+
"--all-mlx-drafter", "--code-prompts", "--cuda-trim",
252+
"--single-fused",
253+
"--n-samples", "{n_samples}",
254+
"--max-new-tokens", "{max_new_tokens}",
255+
"--block-size", "{block_size}",
256+
"--prefill-chunk-size", "512",
257+
"--output",
258+
"results/research/k3_mac_bridge_k3_fused_singlefused_probe.json",
259+
),
260+
),
261+
timeout_minutes=60,
262+
params={
263+
"n_samples": ("int:n_samples", "2"),
264+
"max_new_tokens": ("int:max_new_tokens", "16"),
265+
"block_size": ("int:block_size", "4"),
266+
},
267+
validate_reports=False,
268+
),
238269
Preset(
239270
name="k3-fused-allmlx-code-trim",
240271
description="CUDA-parity rollback test: all-MLX fused + --cuda-trim "

scripts/research/k3_integrated_niah_eval_mac.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ def parse_args() -> argparse.Namespace:
112112
"crossings per block. Requires --s5-exact-full-attn "
113113
"(the all-MLX path uses native-S5 injection; the "
114114
"f_theta sliding restoration path stays torch).")
115+
ap.add_argument("--single-fused", action="store_true",
116+
help="PROBE: with --cuda-trim, fuse drafter+verifier into ONE "
117+
"graph (skip the two-phase eval) to classify the Metal "
118+
"instability (fundamental command-buffer vs fixable SDPA "
119+
"fallback). Reports per-block eval times.")
115120
ap.add_argument("--cuda-trim", action="store_true",
116121
help="All-MLX fused with the CUDA-parity rollback: all-KVCache "
117122
"verifier layout + native trim_prompt_cache (keep accepted "
@@ -731,7 +736,8 @@ def eval_fused_specdecode() -> Tuple[List[str], List[float], List[int]]:
731736
adapter, active_drafter, aux_prompt=aux_prompt,
732737
embed_fn=embed_fn, lm_head_fn=lm_head_fn,
733738
gen_tokens=args.max_new_tokens,
734-
block_size=args.block_size, eos_ids=end_ids)
739+
block_size=args.block_size, eos_ids=end_ids,
740+
single_fused=args.single_fused)
735741
elif mlx_drafter is not None:
736742
# Single-sync all-MLX loop (levers ①②③) + v3 carry rollback.
737743
res = fused_specdecode_generate_mlx(

0 commit comments

Comments
 (0)