Skip to content

PN59 streaming-GDN orchestrator never engages on chunked-prefill — Cliff 2b still fires on 1× RTX 3090 #22

Description

@noonghunna

PN59 streaming-GDN orchestrator never engages on chunked-prefill — Cliff 2b still fires on 1× RTX 3090

Summary

PN59's eligibility gate rejects single-seq calls when chunk_indices or chunk_offsets is non-None, falling back to _vanilla_path — which then OOMs at the exact chunk_o.py:161 o = torch.empty_like(v) site PN59 was advertised to eliminate. On a 24 GB single-card config, vLLM's chunked-prefill is mandatory to fit anything, so chunk_indices/chunk_offsets are always populated → PN59's protection envelope excludes the path that actually needs it.

Cliff 2b is unchanged on club-3090's long-text.yml config vs v7.69 baseline.

Repro

  • Hardware: 1× RTX 3090, 24 GB VRAM, sm_86
  • Pin: Genesis 7b9fd319 (v7.72.2), vLLM 0.20.2rc1.dev9+g01d4d1ad3 (allowlist entry patch_genesis_unified.py #2)
  • Compose: noonghunna/club-3090 models/qwen3.6-27b/vllm/compose/docker-compose.long-text.yml (TP=1, qwen3.6-27b-AutoRound INT4, MTP K=3, KV turboquant_3bit_nc, max_model_len=180000, gpu-memory-utilization=0.93, max-num-batched-tokens=4128)
  • Trigger: single-shot 60K-token prompt (Python webhook code filler)
  • GENESIS_ENABLE_PN59_STREAMING_GDN=1 in env
HTTP 500 within 18s
torch.OutOfMemoryError: tried to allocate 50 MiB, 56 MiB free
File ".../fla/ops/chunk_o.py", line 161, in chunk_fwd_o
    o = torch.empty_like(v)

Root cause (PN59 IS firing, but bypasses to vanilla then OOMs)

PN59's text-patch is correctly applied to chunk.py at boot — verified via in-container head of chunk_gated_delta_rule_fwd. The streaming_gdn_driver module imports cleanly. GdnScratchPool.is_production_eligible() returns True. threshold_T = 4 × 64 × 4 = 1024; our T=4128 (per chunked-prefill chunk) and T=60000 (full prefill) both exceed the threshold.

After injecting an exception logger into PN59's silent except Exception: pass, the actual flow surfaced:

chunk.py:47               → return _genesis_pn59_streaming(...)        [dispatcher fires]
streaming_gdn_driver.py:118 → return _vanilla_path(...)                [eligibility REJECTED]
streaming_gdn_driver.py:195 → o = chunk_fwd_o(...)                     [internal vanilla path]
chunk_o.py:161             → o = torch.empty_like(v)                  [OOM]
                             OutOfMemoryError propagates → outer
                             chunk.py except: pass catches it
                             → falls through to on-disk vanilla
                             → OOMs again at chunk_o.py:161
                             → user-facing 500

Single-seq is true, production_eligible is true, T>threshold is true — the only remaining condition is has_no_chunk_metadata. v7.72.2's streaming_gdn_driver.py comment confirms this gate was tightened in the v7.72 audit:

# Audit P2 fix 2026-05-05 (genesis_deep_cross_audit, P2.4): tightened
# eligibility to also reject calls with non-trivial chunk metadata.
# The streaming path passes `cu_seqlens=None, chunk_indices=None,
# chunk_offsets=None` to inner kernels; if the caller supplied
# significant chunk_offsets/chunk_indices we'd silently drop that
# metadata and produce divergent results vs vanilla.

The audit fix is correct in principle (avoid silent divergence) but on serving-time vLLM, chunked-prefill always populates these fields when max-num-batched-tokens < prompt_len, which on a 24 GB card is mandatory to load 60K-token prompts at all.

Net: PN59 protects a path that doesn't actually run on Ampere consumer + long context.

Diagnosis surface

  • PN59 dispatcher is reached (proven by injected logger trapping the OOM raised from _vanilla_path)
  • has_no_chunk_metadata is False during chunked-prefill — most likely the failing eligibility condition (single-seq + production_eligible + T>threshold are all confirmed True via direct probe)
  • except Exception: pass masks the OOM so operators can't tell PN59 silently bypassed; we only saw it after the on-disk vanilla also OOM'd

Reproducer assets

  • Compose: models/qwen3.6-27b/vllm/compose/docker-compose.long-text.yml (in noonghunna/club-3090, branch v7.72.2-uplift)
  • 60K probe script: paste below or /tmp/pn59_high_t_probe.py
  • Boot env shows: [Genesis Dispatcher] APPLY PN59 — Streaming-GDN orchestrator (Variant D Phase 2)
# /tmp/pn59_high_t_probe.py — single-shot 60K-token prompt to long-text.yml
# (60K Python code filler + 100-token output budget)
import json, time, urllib.request, subprocess
ENDPOINT, MODEL = "http://localhost:8020", "qwen3.6-27b-autoround"
filler = ("def handle_webhook(p): ...\n" * 12000)[:240000]
req = urllib.request.Request(
    f"{ENDPOINT}/v1/chat/completions",
    data=json.dumps({"model": MODEL, "messages": [
        {"role": "system", "content": "Reply concisely."},
        {"role": "user", "content": "Find bugs:\n```python\n" + filler + "\n```"},
    ], "max_tokens": 200, "temperature": 0.4}).encode(),
    headers={"Content-Type": "application/json"}, method="POST",
)
with urllib.request.urlopen(req, timeout=600) as r:
    print(r.status, r.read()[:200])

Fix proposals (operator → upstream priority)

  1. Make has_no_chunk_metadata rejection optional — gate it behind a separate env (GENESIS_PN59_STRICT_NO_METADATA=1, default off) so single-seq chunked-prefill takes the streaming path. The "silent metadata drop" risk the audit was guarding against is real but at minimum the operator should be able to opt-in to "stream anyway, accept the metadata-drop risk" since the alternative is a hard OOM.

  2. Thread chunk_indices/chunk_offsets into _streaming_path — the structurally correct fix. Streaming would respect the chunk metadata across windows.

  3. Surface the eligibility-bypass reason without GENESIS_PN59_DEBUG=1 — the silent except Exception: pass in chunk.py:54 masks the OOM raised from _vanilla_path. At minimum a logger.info of the bypass reason so operators don't guess. (Possibly: log only when streaming is enabled-but-bypassed, to keep noise low on configs that don't engage PN59 at all.)

  4. Update the v7.72 release-notes claim"Single 24 GiB card now sustains 92K-context generation for an hour without OOM" doesn't hold on Ampere consumer + chunked-prefill (which is the only way 92K-context fits on a 24 GB card). Either restrict the claim to long-prefill-without-chunked-prefill, or document the missing protection envelope.

What I'd like to confirm

If has_no_chunk_metadata is the failing gate (high probability per the source comment), a one-line A/B with that condition relaxed to True should make PN59 actually engage on this rig, and the OOM should disappear. Happy to run that A/B if you want to confirm before designing the proper fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions