Skip to content

feat(server): --parallel-ctx-threshold admission gate + UM prefetch net (hydra#747 baseline) - #110

Merged
ddvnguyen merged 5 commits into
baselinefrom
fork/hydra-747-parallel-ctx-threshold-baseline
Sep 9, 2026
Merged

feat(server): --parallel-ctx-threshold admission gate + UM prefetch net (hydra#747 baseline)#110
ddvnguyen merged 5 commits into
baselinefrom
fork/hydra-747-parallel-ctx-threshold-baseline

Conversation

@ddvnguyen

Copy link
Copy Markdown
Owner

Hydra issue: ddvnguyen/hydra_vortex#747 · baseline delivery (clean v0.4.0 5266f24da + 2 commits, no hydra-fork patches)

Commits

  1. feat(server): --parallel-ctx-threshold N (env LLAMA_ARG_PARALLEL_CTX_THRESHOLD, server-only): a queued request is admitted into an auto-selected free slot only if resident-ctx-sum + candidate-prompt < N; otherwise deferred (queue_tasks.defer) and retried on slot release via pop_deferred_task. Explicit id_slot >= 0 bypasses. Vanilla accounting (no hydra fields): per processing slot, max(prompt-cache tokens, full task prompt length) — the max covers early prefill (cache small, task long) and deep generation.
  2. perf(cuda): UM advise/prefetch net (T3 deliverable, cherry-picked unchanged): cudaMemAdvise(SetPreferredLocation) + cudaMemPrefetchAsync for managed allocations.

The RPC reconnect-loop fix is NOT here — that bug lives in hydra-fork's ggml-org#470 reconnection machinery which doesn't exist on baseline; it shipped separately via #109 (hydra-fork).

Rig verification (2×RTX: 5060 Ti CUDA0 + 3060 RPC CUDA1, Qwen3.8-27B-UD-Q5_K_M, tensor-split 27,38, UM on)

  • Build requirement: -DGGML_CUDA_FA_ALL_QUANTS=ON is mandatory for K q8_0 / V q4_1|q5_1 flash-attn on v0.4.0 — without it the RPC peer GGML_ABORTs at fattn.cu:707 on first FA op (documented in arm093's yml; cost me one boot).
  • Gate semantics (threshold 100000, parallel=2, per-slot fences 148000, kv_unified off, V q5_1): defer fired defer task 563 (resident 88857 + candidate 68727 >= threshold 100000); first task completed and released → deferred task auto-admitted and prefilled to completion (405 t/s). 0 spurious defers across all boots/loops.
  • 2×small concurrent (threshold 100000): full window overlap, no defers.
  • Single-request decode: mean 38.4 t/s (kv_unified on, V q4_1) and 40.1 t/s (kv_unified off, V q5_1) — at the arm090 ~37-40 bar on clean v0.4.0.
  • arm102 harness-parity retest (see hydra_vortex 740-results-report.md): single 40.1 ✓; n=2 concurrent 49.2→52.6 agg symmetric — ⚠️ GGML_CUDA_FORCE_CUBLAS=ON halves concurrent decode (25.2 vs 49.2-52.6 agg) and causes per-slot draft-acceptance asymmetry (0.51 vs 0.99); build with it OFF.

Do not merge before the hydra_vortex parent PR lands its submodule bump. No deploy workflow triggered.

ddvnguyen and others added 2 commits September 8, 2026 20:21
Advise read-mostly + prefetch to the allocating device for unified-memory
buffers so lazily-migrated pages settle on the owning GPU.

Co-Authored-By: opencode <noreply@opencode.ai>
Defer queued requests when resident-ctx-sum + candidate prompt would
reach the threshold; deferred tasks retry on slot release. Auto-slot
path only (explicit id_slot bypasses). Vanilla slot accounting:
max(prompt-cache tokens, full task prompt length) per processing slot.
Env: LLAMA_ARG_PARALLEL_CTX_THRESHOLD.

Co-Authored-By: opencode <noreply@opencode.ai>
ddvnguyen added a commit to ddvnguyen/hydra_vortex that referenced this pull request Sep 8, 2026
… (hydra#747)

Per #747 direction: the baseline delivery targets ddvnguyen/llama.cpp's
'baseline' branch (clean v0.4.0 5266f24da) — admission gate ported with
vanilla slot accounting (no hydra fields) + UM prefetch net. RPC
reconnect-loop fix ships separately via ddvnguyen/llama.cpp#109
(hydra-fork), not bundled here.

Fork PR: ddvnguyen/llama.cpp#110

Co-Authored-By: opencode <noreply@opencode.ai>
ddvnguyen and others added 3 commits September 8, 2026 23:43
Mirror the defer SRV_INF with an admit line (resident + candidate <
threshold) so boundary tests can show the exact accounting either way.

Co-Authored-By: opencode <noreply@opencode.ai>
… exceeding threshold alone

The gate deferred any request with resident + candidate >= threshold,
including resident == 0. A lone request bigger than the threshold on an
otherwise idle pool then defers forever: nothing is resident, so no slot
release ever retries it. The threshold guards combined oversubscription
between concurrent requests, not a single request's own size (that is
bounded by the per-slot cap). Defer now requires resident > 0; a lone
request always admits. Admit log distinguishes the lone case so the
printed comparison stays accurate.

Co-Authored-By: opencode <noreply@opencode.ai>
… one task

A release event re-posted a single deferred task (FIFO head, or one
explicitly requesting the slot). If that task re-deferred - e.g. the
hydra#747 threshold gate deferring a large candidate - the release was
burned and every other waiter stayed queued even with a slot idle, while
brand-new arrivals kept getting served from the main queue (priority
inversion, silent client hangs).

Re-post the entire deferred FIFO in order (explicit-slot matches first)
to the front of the main queue so every waiter is re-evaluated against
the freed capacity before newer arrivals; tasks that still cannot
proceed re-defer to the back, preserving FIFO order.

Deterministic repro + production case: hydra#747 task-12239 hang
(docs/investigations/740-results-report.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ddvnguyen
ddvnguyen merged commit d50efc6 into baseline Sep 9, 2026
11 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant