Found during a source review of the v20 r12 release image voipmonitor/vllm:gilded-gnosis-v20-vllmb46c3aa-si35aebc6-fi801d57a-cu132-20260730-r12. All cited sparkinfer files are byte-identical to public commit de04125508c75e748016493f851e6bd4513b515b (git diff de04125 r12-composed -- <path> empty for kernel.py, fused_moe/_impl.py, _lib/compiler.py, w4a16/route_pack.py, w4a16/host.py; composed tree 35aebc6), so permalinks use that SHA. vLLM caller refs are from the composed r12 vLLM tree (commit b31667d3, tree b46c3aa) and are quoted inline without permalinks.
Deployment context: GLM-5.2 753B MoE (hidden 6144, 75 MoE layers, top-k 8), EXL3 TR3 3.0 bpw, TP4, cudagraph_mode=FULL_AND_PIECEWISE; launcher defaults VLLM_EXL3_TRELLIS_MAX_M=32, VLLM_EXL3_TRELLIS_BLOCK_M=8, VLLM_EXL3_PREFILL_TRELLIS=1, VLLM_EXL3_PREFILL_BLOCK_M=64, MAX_BATCHED_TOKENS=3072.
This is a defensive-hardening report, filed with an honest reachability verdict: the hazard is LATENT on both shipped configs, not live. It is filed anyway because (1) the compile entry point demonstrably sits inside the captured region on the shipped EXL3 path, (2) every layer of protection is external to sparkinfer while every sibling first-use site in the same file self-defends, (3) the one in-process backstop is bypassed on a disk-cache hit, and (4) the fix as originally proposed is wrong at the proposed location.
Where
compile_w4a16_fused_moe is consulted on every run_w4a16_moe call, including from inside a captured CUDA graph, and has no capture guard: sparkinfer/moe/_shared/kernels/w4a16/kernel.py#L10246-L10259
if int(prepared.workspace.numel()) < sms * 4 + 2:
raise ValueError("prepared W4A16 workspace is too small for fused FC1+FC2")
if fused_launch is None:
fused = compile_w4a16_fused_moe(
size_m=m,
hidden_size=hidden_size,
...
moe_block_size=block_size_m,
max_m_blocks=int(required_m_blocks),
fused_launch is always None on the vLLM path — sparkinfer/moe/fused_moe/_impl.py#L937-L959 (TPMoEScratchPlan.bind):
route_expert_map=route_expert_map,
output_expert_map=output_expert_map,
fused_launch=None,
topk_sum_launch=None,
)
The whole of compile_w4a16_fused_moe (kernel.py:7026-7731) contains no capture check. Exhaustive listing of is_current_stream_capturing in kernel.py — three occurrences, none in either compile entry point nor at the resolve site:
8446 if torch.cuda.is_current_stream_capturing(): # rotation placeholder
9179 if torch.cuda.is_current_stream_capturing(): # _get_c_tmp scratch
9439 if torch.cuda.is_current_stream_capturing(): # Trellis dense storage
Contrast the planner, which does guard — sparkinfer/moe/fused_moe/_impl.py#L6183-L6186:
if not torch.cuda.is_available():
raise RuntimeError("CUDA must be available before planning Trellis MoE")
if torch.cuda.is_current_stream_capturing():
raise RuntimeError("Trellis launch planning cannot run during capture")
and the sibling first-use compile sites elsewhere in the tree that all self-defend: gemm/_shared/mxfp8_bmm.py:1393 and :1490 (raise on "compile miss during CUDA-graph capture"), gemm/mla_query_projection/_bf16.py:199-203, gemm/bf16_gemv/_kernel.py:289-292 (falls back to cuBLAS under capture), w4a16/route_pack.py:96, moe/ep_moe/_impl.py:106, moe/fused_moe/_impl.py:5900 and :5964. The largest and slowest compile in the MoE stack (the fused FC1→act→FC2 megakernel plus two GEMM kernels) is the one hot-path first-use site with no guard.
Mechanism
bind() hard-codes fused_launch=None, so the launch is resolved lazily inside the kernel call rather than being carried on the binding. The captured graph body therefore executes compile_w4a16_fused_moe(...) as Python on every MoE layer.
- The requested cache key is
("w4a16_fused_moe", device, kernel.__cache_key__) (kernel.py:7423-7428). size_m is not in the key (it is dataclasses.replaced onto a hit at L7430-7435), but max_m_blocks, moe_block_size, cta_m_blocks, blocks_per_sm, tile_n/tile_k and shared_words of both GEMMs are (kernel.py:1000-1037, nested via L4810-4811).
max_m_blocks comes from required_m_blocks = int(block_expert_ids.numel()) (kernel.py:10201), which pack_topk_routes_by_expert slices to capacity_route_blocks derived from route_pack_numel_capacity(numel, topk) → route_pack_token_capacity → 1 << (tokens-1).bit_length() (host.py:232-240, route_pack.py:307-331, route_pack.py:108-112). So the key is a deterministic function of the next-pow2 token bucket of m, plus host-side constants.
- On a
_FUSED_CACHE miss (kernel.py:6606, an unbounded module-level dict) the function proceeds straight into sparkinfer_compile(...) (kernel.py:7659, 8146, 8236) → sparkinfer._lib.compiler.compile, i.e. an NVVM/ptxas JIT plus CUDA module load, with a CUDA graph capture in progress.
Reachability (LATENT on both shipped configs)
I tried to construct a live path and could not. Recorded in full so the margin is auditable:
The entry point is genuinely inside capture. Shipped EXL3 MoE routes exl3.py:_apply_rank_sliced → api.bind(plan, scratch=...) → TPMoEScratchPlan.bind (fused_launch=None) → run_w4a16_moe, for every decode step, inside the FULL decode graphs. Not eager-only.
But vLLM warms every key first. GPUModelRunner._warmup_and_capture (vllm/v1/worker/gpu_model_runner.py:6913-6938) runs cudagraph_num_of_warmups eager _dummy_runs at exactly desc.num_tokens immediately before capturing that descriptor, and vllm/config/vllm.py:1430 forces cudagraph_num_of_warmups = 1 whenever cudagraphs are enabled (the CompilationConfig default at config/compilation.py:643 is 0). The FULL path adds a second eager re-warm (vllm/v1/worker/gpu/cudagraph_utils.py:500-509), and CUDAGraphWrapper optionally a third (vllm/compilation/cuda_graph.py:317-329). Since the key is a pure function of m = desc.num_tokens plus constants, and _FUSED_CACHE is unbounded (no eviction between warmup and capture), every key capture requests was already populated eagerly.
Recomputed key coverage (top-k 8, E=160 local experts, max_m_blocks(m) = ceil(max_packed_route_slots(next_pow2(m)*topk, bs, E) / bs); capture sizes are MTP+1 .. GRAPH in steps of MTP+1, serve-glm52-v16.sh:337-345):
| capture m |
pow2 bucket |
plan |
bs |
max_m_blocks |
| 4 |
4 |
decode |
8 |
32 |
| 8 |
8 |
decode |
8 |
64 |
| 12, 16 |
16 |
decode |
8 |
128 |
| 20, 24, 28, 32 |
32 |
decode |
8 |
172 |
36…64 (m > TRELLIS_MAX_M=32) |
64 |
prefill |
64 |
166 |
Five distinct GEMM keys for a GRAPH=32 capture set; six with GRAPH=64. Per-size eager warmup is therefore load-bearing, not incidental — nothing else populates these.
Second line of defence, also external. Both capture sites wrap torch.cuda.graph(...) in guard_b12x_kernel_resolution (vllm/compilation/cuda_graph.py:332-341 for PIECEWISE, vllm/v1/worker/gpu/cudagraph_utils.py:510-516 for FULL), which calls sparkinfer.freeze_kernel_resolution. It is active on the shipped configs because serve-glm52-v16.sh:227-239 unconditionally exports VLLM_USE_B12X_WO_PROJECTION=1, VLLM_USE_B12X_MHC=1, VLLM_USE_B12X_SPARSE_INDEXER=1 (plus VLLM_USE_B12X_MOE=1 for MOE_BACKEND=b12x), satisfying b12x_cuda_graph_prewarm_enabled(). A genuine cute.compile inside capture therefore hits raise_if_kernel_resolution_frozen (_lib/compiler.py:2727 / :2777) → KernelResolutionFrozenError.
Where the margin is thin — three independent ways it disappears:
- The freeze does not cover a disk-cache hit.
_lib/compiler.py:2659-2679 returns the loaded object before raise_if_kernel_resolution_frozen is ever reached:
if disk_cache_enabled:
compiled = _load_cute_compile_from_disk(cache_key)
if compiled is not None:
...
_memory_cache_put(memory_cache_key, compiled)
return compiled
SPARKINFER_COMPILE_DISK_CACHE defaults to "1" (_lib/compiler.py:655-657) and the image pins a persistent SPARKINFER_COMPILE_CACHE_DIR=/cache/jit/${CACHE_FINGERPRINT}/sparkinfer/compile (Dockerfile.vllm-b12x-cu132:1234). So on a warm host cache a first-touch-under-capture takes _load_cute_compile_from_disk → tempfile.TemporaryDirectory + shutil.copy2 + ExternalBinaryModule(...) (a CUDA module load) inside the capture region, silently, with no diagnostic at all.
- Protection (i) is a vLLM upstream default, not a sparkinfer invariant:
cudagraph_num_of_warmups is 0 in CompilationConfig, and any embedder of sparkinfer that captures without an eager pass at the same m has zero protection.
- Protection (ii) is gated on
VLLM_USE_B12X_*; a config with all of them 0 silently drops the freeze.
Failure mode if it fires
Two sub-cases, honestly separated:
- Disk-cache miss:
raise_if_kernel_resolution_frozen fires first (when the freeze is active) → clean KernelResolutionFrozenError before any CUDA work. Without the freeze: a multi-hundred-ms-to-seconds NVVM/ptxas JIT plus module load inside the capture window.
- Disk-cache hit: module load inside capture, freeze bypassed. Whether
ExternalBinaryModule construction performs work that CUDA rejects during stream capture (and hence whether cudaStreamEndCapture fails loudly or a subtly broken graph is produced) is not statically provable from this tree — ExternalBinaryModule lives in cutlass.base_dsl.export, outside both repos. Measurement needed (see Verification below). I am not claiming silent corruption.
The project already treats compile-inside-capture as a defect class, but only in offline validation, never at runtime: validation/cutlass_migration/acceptance/w4a16_serving.py:416-418 ("graph capture compiled a CUTLASS kernel"), .../compute_exceptions.py:748-749, .../residual_composite.py:558-560, .../diagnostics/paired/w4a16_serving.py:820.
Severity
bug — defensive. No silent numeric corruption is demonstrated, and no live path exists on the shipped gate or turnkey configs. The impact is a missing invariant on the one unguarded first-use compile that provably executes inside captured graphs, whose entire safety today is supplied by the host framework. Cost when it fires ranges from a loud startup error (best case) to an unaudited CUDA module load inside a capture region (disk-hit case). No memory cost, inside or outside the profiled budget.
Suggested fix
Not the obvious one. Mirroring the planner's guard inside compile_w4a16_fused_moe would be wrong: _prewarm_w4a16_planned_launches deliberately calls it while capturing — _impl.py#L6519-L6520 captures is_capturing = torch.cuda.is_current_stream_capturing() and then at #L6625-L6643 skips only the route-pack launch under capture, keeping the compile_w4a16_fused_moe call at #L6565 on purpose ("The real route-pack launch happens in run_w4a16_moe. During CUDA graph capture this prewarm-only launch would be recorded as useless work"). A guard in the compile function would break that supported path.
Correct placement is the runtime resolve site, where the miss is unintended:
if fused_launch is None:
if torch.cuda.is_current_stream_capturing():
key_probe = _fused_moe_cache_probe(...) # existing key, no compile
if key_probe is None:
raise RuntimeError(
"W4A16 fused MoE launch is not resolved for CUDA graph capture "
f"(m={m}, moe_block_size={block_size_m}, "
f"max_m_blocks={required_m_blocks}); run an eager warmup at this "
"token count before capturing"
)
fused = compile_w4a16_fused_moe(...)
Raising is right here, not falling back: unlike _get_c_tmp (kernel.py:9164-9184, which raises under capture and allocates in eager) and unlike bf16_gemv/_kernel.py:290 (which has a legitimate cuBLAS fallback), there is no alternative MoE launch to fall back to — the only options are compile or fail, and compiling is what we are trying to prevent. It is also capture-safe (pure host-side dict probe, no CUDA API call, no allocation) and changes neither numerics nor any ABI. Cheapest correct variant: cache-probe first and raise only on a genuine miss, so the guard never rejects a warm key.
Two adjacent items, deliberately out of scope here:
Verification / repro hint
- Prove the entry point is inside capture (no GPU needed beyond a normal boot): set
SPARKINFER_LOG_CUTE_COMPILES_AFTER_ENGINE_START=1 and boot the turnkey; every compile_w4a16_fused_moe disk-hit/miss during Capturing CUDA graphs is logged with its cache key.
- Prove the latency of the warmup dependency: launch with
--compilation-config '{"cudagraph_num_of_warmups":0,...}' (overriding config/vllm.py:1430) plus VLLM_USE_B12X_MOE=0 VLLM_USE_B12X_MHC=0 VLLM_USE_B12X_WO_PROJECTION=0 VLLM_USE_B12X_SPARSE_INDEXER=0 to drop the freeze, and SPARKINFER_COMPILE_DISK_CACHE=0 to force a true compile. Capture should then attempt a CuTe compile for the largest capture size. Observe whether cudaStreamEndCapture fails or a graph is produced — this is the measurement that resolves the "silent broken graph vs. loud error" question above.
- Unit-level: the harness pattern already exists — assert
compile_cache_info()["compile_misses"] is unchanged across a capture, as validation/cutlass_migration/acceptance/w4a16_serving.py:405-418 does; extend it to the fused_launch=None bind path.
Provenance of the observation
First noted (unfiled) as "Latent capture hazard" in a review comment on the unrelated perf issue #96: #96 (comment) — parent issue #96 (W4A16 launch-path per-call cost / unreachable prewarm) is linked for context only; this guard gap is not covered by #96's body.
Found during a source review of the v20 r12 release image
voipmonitor/vllm:gilded-gnosis-v20-vllmb46c3aa-si35aebc6-fi801d57a-cu132-20260730-r12. All cited sparkinfer files are byte-identical to public commitde04125508c75e748016493f851e6bd4513b515b(git diff de04125 r12-composed -- <path>empty forkernel.py,fused_moe/_impl.py,_lib/compiler.py,w4a16/route_pack.py,w4a16/host.py; composed tree35aebc6), so permalinks use that SHA. vLLM caller refs are from the composed r12 vLLM tree (commitb31667d3, treeb46c3aa) and are quoted inline without permalinks.Deployment context: GLM-5.2 753B MoE (hidden 6144, 75 MoE layers, top-k 8), EXL3 TR3 3.0 bpw, TP4,
cudagraph_mode=FULL_AND_PIECEWISE; launcher defaultsVLLM_EXL3_TRELLIS_MAX_M=32,VLLM_EXL3_TRELLIS_BLOCK_M=8,VLLM_EXL3_PREFILL_TRELLIS=1,VLLM_EXL3_PREFILL_BLOCK_M=64,MAX_BATCHED_TOKENS=3072.This is a defensive-hardening report, filed with an honest reachability verdict: the hazard is LATENT on both shipped configs, not live. It is filed anyway because (1) the compile entry point demonstrably sits inside the captured region on the shipped EXL3 path, (2) every layer of protection is external to sparkinfer while every sibling first-use site in the same file self-defends, (3) the one in-process backstop is bypassed on a disk-cache hit, and (4) the fix as originally proposed is wrong at the proposed location.
Where
compile_w4a16_fused_moeis consulted on everyrun_w4a16_moecall, including from inside a captured CUDA graph, and has no capture guard:sparkinfer/moe/_shared/kernels/w4a16/kernel.py#L10246-L10259fused_launchis alwaysNoneon the vLLM path —sparkinfer/moe/fused_moe/_impl.py#L937-L959(TPMoEScratchPlan.bind):The whole of
compile_w4a16_fused_moe(kernel.py:7026-7731) contains no capture check. Exhaustive listing ofis_current_stream_capturinginkernel.py— three occurrences, none in either compile entry point nor at the resolve site:Contrast the planner, which does guard —
sparkinfer/moe/fused_moe/_impl.py#L6183-L6186:and the sibling first-use compile sites elsewhere in the tree that all self-defend:
gemm/_shared/mxfp8_bmm.py:1393and:1490(raise on "compile miss during CUDA-graph capture"),gemm/mla_query_projection/_bf16.py:199-203,gemm/bf16_gemv/_kernel.py:289-292(falls back to cuBLAS under capture),w4a16/route_pack.py:96,moe/ep_moe/_impl.py:106,moe/fused_moe/_impl.py:5900and:5964. The largest and slowest compile in the MoE stack (the fused FC1→act→FC2 megakernel plus two GEMM kernels) is the one hot-path first-use site with no guard.Mechanism
bind()hard-codesfused_launch=None, so the launch is resolved lazily inside the kernel call rather than being carried on the binding. The captured graph body therefore executescompile_w4a16_fused_moe(...)as Python on every MoE layer.("w4a16_fused_moe", device, kernel.__cache_key__)(kernel.py:7423-7428).size_mis not in the key (it isdataclasses.replaced onto a hit at L7430-7435), butmax_m_blocks,moe_block_size,cta_m_blocks,blocks_per_sm,tile_n/tile_kandshared_wordsof both GEMMs are (kernel.py:1000-1037, nested via L4810-4811).max_m_blockscomes fromrequired_m_blocks = int(block_expert_ids.numel())(kernel.py:10201), whichpack_topk_routes_by_expertslices tocapacity_route_blocksderived fromroute_pack_numel_capacity(numel, topk)→route_pack_token_capacity→1 << (tokens-1).bit_length()(host.py:232-240,route_pack.py:307-331,route_pack.py:108-112). So the key is a deterministic function of the next-pow2 token bucket ofm, plus host-side constants._FUSED_CACHEmiss (kernel.py:6606, an unbounded module-leveldict) the function proceeds straight intosparkinfer_compile(...)(kernel.py:7659, 8146, 8236) →sparkinfer._lib.compiler.compile, i.e. an NVVM/ptxas JIT plus CUDA module load, with a CUDA graph capture in progress.Reachability (LATENT on both shipped configs)
I tried to construct a live path and could not. Recorded in full so the margin is auditable:
The entry point is genuinely inside capture. Shipped EXL3 MoE routes
exl3.py:_apply_rank_sliced→api.bind(plan, scratch=...)→TPMoEScratchPlan.bind(fused_launch=None) →run_w4a16_moe, for every decode step, inside the FULL decode graphs. Not eager-only.But vLLM warms every key first.
GPUModelRunner._warmup_and_capture(vllm/v1/worker/gpu_model_runner.py:6913-6938) runscudagraph_num_of_warmupseager_dummy_runs at exactlydesc.num_tokensimmediately before capturing that descriptor, andvllm/config/vllm.py:1430forcescudagraph_num_of_warmups = 1whenever cudagraphs are enabled (theCompilationConfigdefault atconfig/compilation.py:643is0). The FULL path adds a second eager re-warm (vllm/v1/worker/gpu/cudagraph_utils.py:500-509), andCUDAGraphWrapperoptionally a third (vllm/compilation/cuda_graph.py:317-329). Since the key is a pure function ofm=desc.num_tokensplus constants, and_FUSED_CACHEis unbounded (no eviction between warmup and capture), every key capture requests was already populated eagerly.Recomputed key coverage (top-k 8, E=160 local experts,
max_m_blocks(m) = ceil(max_packed_route_slots(next_pow2(m)*topk, bs, E) / bs); capture sizes areMTP+1 .. GRAPHin steps ofMTP+1,serve-glm52-v16.sh:337-345):bsmax_m_blocksm > TRELLIS_MAX_M=32)Five distinct GEMM keys for a
GRAPH=32capture set; six withGRAPH=64. Per-size eager warmup is therefore load-bearing, not incidental — nothing else populates these.Second line of defence, also external. Both capture sites wrap
torch.cuda.graph(...)inguard_b12x_kernel_resolution(vllm/compilation/cuda_graph.py:332-341for PIECEWISE,vllm/v1/worker/gpu/cudagraph_utils.py:510-516for FULL), which callssparkinfer.freeze_kernel_resolution. It is active on the shipped configs becauseserve-glm52-v16.sh:227-239unconditionally exportsVLLM_USE_B12X_WO_PROJECTION=1,VLLM_USE_B12X_MHC=1,VLLM_USE_B12X_SPARSE_INDEXER=1(plusVLLM_USE_B12X_MOE=1forMOE_BACKEND=b12x), satisfyingb12x_cuda_graph_prewarm_enabled(). A genuinecute.compileinside capture therefore hitsraise_if_kernel_resolution_frozen(_lib/compiler.py:2727/:2777) →KernelResolutionFrozenError.Where the margin is thin — three independent ways it disappears:
_lib/compiler.py:2659-2679returns the loaded object beforeraise_if_kernel_resolution_frozenis ever reached:SPARKINFER_COMPILE_DISK_CACHEdefaults to"1"(_lib/compiler.py:655-657) and the image pins a persistentSPARKINFER_COMPILE_CACHE_DIR=/cache/jit/${CACHE_FINGERPRINT}/sparkinfer/compile(Dockerfile.vllm-b12x-cu132:1234). So on a warm host cache a first-touch-under-capture takes_load_cute_compile_from_disk→tempfile.TemporaryDirectory+shutil.copy2+ExternalBinaryModule(...)(a CUDA module load) inside the capture region, silently, with no diagnostic at all.cudagraph_num_of_warmupsis0inCompilationConfig, and any embedder of sparkinfer that captures without an eager pass at the samemhas zero protection.VLLM_USE_B12X_*; a config with all of them0silently drops the freeze.Failure mode if it fires
Two sub-cases, honestly separated:
raise_if_kernel_resolution_frozenfires first (when the freeze is active) → cleanKernelResolutionFrozenErrorbefore any CUDA work. Without the freeze: a multi-hundred-ms-to-seconds NVVM/ptxas JIT plus module load inside the capture window.ExternalBinaryModuleconstruction performs work that CUDA rejects during stream capture (and hence whethercudaStreamEndCapturefails loudly or a subtly broken graph is produced) is not statically provable from this tree —ExternalBinaryModulelives incutlass.base_dsl.export, outside both repos. Measurement needed (see Verification below). I am not claiming silent corruption.The project already treats compile-inside-capture as a defect class, but only in offline validation, never at runtime:
validation/cutlass_migration/acceptance/w4a16_serving.py:416-418("graph capture compiled a CUTLASS kernel"),.../compute_exceptions.py:748-749,.../residual_composite.py:558-560,.../diagnostics/paired/w4a16_serving.py:820.Severity
bug— defensive. No silent numeric corruption is demonstrated, and no live path exists on the shipped gate or turnkey configs. The impact is a missing invariant on the one unguarded first-use compile that provably executes inside captured graphs, whose entire safety today is supplied by the host framework. Cost when it fires ranges from a loud startup error (best case) to an unaudited CUDA module load inside a capture region (disk-hit case). No memory cost, inside or outside the profiled budget.Suggested fix
Not the obvious one. Mirroring the planner's guard inside
compile_w4a16_fused_moewould be wrong:_prewarm_w4a16_planned_launchesdeliberately calls it while capturing —_impl.py#L6519-L6520capturesis_capturing = torch.cuda.is_current_stream_capturing()and then at#L6625-L6643skips only the route-pack launch under capture, keeping thecompile_w4a16_fused_moecall at#L6565on purpose ("The real route-pack launch happens inrun_w4a16_moe. During CUDA graph capture this prewarm-only launch would be recorded as useless work"). A guard in the compile function would break that supported path.Correct placement is the runtime resolve site, where the miss is unintended:
Raising is right here, not falling back: unlike
_get_c_tmp(kernel.py:9164-9184, which raises under capture and allocates in eager) and unlikebf16_gemv/_kernel.py:290(which has a legitimate cuBLAS fallback), there is no alternative MoE launch to fall back to — the only options are compile or fail, and compiling is what we are trying to prevent. It is also capture-safe (pure host-sidedictprobe, no CUDA API call, no allocation) and changes neither numerics nor any ABI. Cheapest correct variant: cache-probe first and raise only on a genuine miss, so the guard never rejects a warm key.Two adjacent items, deliberately out of scope here:
_lib/compiler.py:2659-2679should consultraise_if_kernel_resolution_frozenbefore the disk-cache load, not after, sofreeze_kernel_resolutionmeans what its name says.capacity_m_blocksfromcapacity_tokens * num_topkexactly (_impl.py:6219-6226) while the runtime resolve uses the pow2 bucket. Recomputed at the turnkey prefill capacity (m=3072,bs=64,topk=8,E=160): plannercapacity_m_blocks = 542, runtimerequired_m_blocks = 670(bucket 4096). So the prewarmed key is unreachable from the runtime probe by two independent mechanisms, not just the tile-config pin.Verification / repro hint
SPARKINFER_LOG_CUTE_COMPILES_AFTER_ENGINE_START=1and boot the turnkey; everycompile_w4a16_fused_moedisk-hit/miss duringCapturing CUDA graphsis logged with its cache key.--compilation-config '{"cudagraph_num_of_warmups":0,...}'(overridingconfig/vllm.py:1430) plusVLLM_USE_B12X_MOE=0 VLLM_USE_B12X_MHC=0 VLLM_USE_B12X_WO_PROJECTION=0 VLLM_USE_B12X_SPARSE_INDEXER=0to drop the freeze, andSPARKINFER_COMPILE_DISK_CACHE=0to force a true compile. Capture should then attempt a CuTe compile for the largest capture size. Observe whethercudaStreamEndCapturefails or a graph is produced — this is the measurement that resolves the "silent broken graph vs. loud error" question above.compile_cache_info()["compile_misses"]is unchanged across a capture, asvalidation/cutlass_migration/acceptance/w4a16_serving.py:405-418does; extend it to thefused_launch=Nonebind path.Provenance of the observation
First noted (unfiled) as "Latent capture hazard" in a review comment on the unrelated perf issue #96: #96 (comment) — parent issue #96 (W4A16 launch-path per-call cost / unreachable prewarm) is linked for context only; this guard gap is not covered by #96's body.