Skip to content

perf(batch): prepared flights, VAD segmentation reuse and CUDA-graph TDT on the GPU worker (+61% L4 bench) - #24384

Merged
Git-on-my-level merged 4 commits into
mainfrom
lane/gpe-port
Nov 2, 2026
Merged

Git-on-my-level merged 4 commits into
mainfrom
lane/gpe-port

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

What changed and why

Productionizes the GPU-efficiency gains from the batch-worker hill climb (avo-lite avo/gpueff), as three ship-on kill switches on the dedicated batch GPU worker. Batch at 100% and moving Japanese off Soniox both need the extra GPU headroom.

Lever Env kill switch (false = off) Helm value Source
Prepare the waiting lane's whole flight within the shared 1800-audio-second decode budget; return finished preparations when the GPU section ends BATCH_PIPELINE_PREPARE_FLIGHT_ENABLED worker.prepareFlightEnabled ticks t5 + t6
Reuse the VAD pass's raw segmentation scores for a full-unit diarization (no second segmentation forward) BATCH_PIPELINE_VAD_SEGMENTATION_REUSE_ENABLED worker.vadSegmentationReuseEnabled tick t8
CUDA-graph TDT decoding, mode asserted around every call (mismatch quarantines) BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED worker.asrCudaGraphsEnabled lane/gpe-cgraph, turned on by the L4 run below

The switches are read once at startup through ProviderConfig, like the shared embedding trunk (#24352). They are registered in config/feature-flags.yaml as ops_kill and added to backend/config/flag_budget.json (count 222) and config/deployment-setting-classification.json.

Not ported:

  • The T4 FP32 ASR fallback (t7): prod batch runs only on L4.
  • The ASR length split (t13): each extra ASR microbatch makes its own dispatch-time spend admission inside the GPU section. Prod's admission stage averages 1.49 s per call; the bench simulates 0.05 s. More calls would cost more GPU-held time than they save.

Product invariants affected

none

How it was verified

Dev bench (gpueff-avo/eval/run.sh): the real worker (load_worker) runs on 248 public-audio units (7055 audio-s) with the prod chart env, 2 lanes and flights of 8. Storage IO is simulated with fixed sleeps. The gate (gate.py) checks no lost speech, no result-class changes, word edit rate ≤1%, speaker agreement ≥0.97 and no words on silent units.

L4 (g2-standard-8, engine-pool, real L4, simulated IO):

run audio-h/GPU-h GPU section s ASR s diarize s CUDA peak GiB
main ×2 70.35 / 70.24 99.9 13.0 19.9 2.10
t5+t6+t8 ×2 113.65 / 112.85 61.7 13.5 15.9 2.10
this PR ×2 112.91 / 112.89 61.7 10.0 15.9 2.25
cgraph tree, eager ×2 / graphs ×2 57.13, 57.02 / 58.77, 58.63 123 / 119.6 13.6 / 10.1 — 2.10 / 2.25

Every run's gate against the L4 main run passed with word edit rate 0.0, speaker agreement 1.0, 0 lost speech and 0 class changes. That is +61% audio-h/GPU-h on L4 against main. CUDA graphs cut ASR by 26% with identical words. On the full stack the bench objective stays flat, because the L4 bench is then bound by its simulated artifact IO, so that saving shows up as less GPU-busy time.

Caveats (simulated IO): the prepared-flight gain comes from overlapping fetches with the GPU section. Prod fetch_decode averages 0.27 s per unit (2 h aggregate) against ~0.43 s simulated, and prod is not saturated. Expect the prod gain from that lever to be smaller than the bench shows, and largest under batch load. VAD reuse and CUDA graphs remove GPU work outright, so they don't depend on IO.

Safety of the early release (t6): the pump cancels its lost-lease watcher on GPU entry and decides refunds (skipped) before dispatch, so releasing finished preparations after the GPU section can't change lease, refund or publication outcomes. Only done futures are closed, and close is idempotent. The decoded-memory bound is unchanged: 1800 s of preparation plus a 7200 s group, and buffers are freed sooner. If any prepared lease is lost while waiting, the whole prepared prefix is refunded with no attempt charge. That costs some throughput, never correctness.

Tests

  • backend/test.sh through omi-heavy, BACKEND_UNIT_TEST_FILE_LIST = all 69 tests/unit/test_batch_pipeline*.py files plus test_batch_worker_vad_recovery.py, test_batch_unit_policy.py and test_flag_budget.py (72 files): all passed on the t5/t6/t8 commit. On the final tree, 71 passed. test_batch_pipeline_admission.py::test_slow_dependency_cannot_grant_after_deadline[seal] failed one wall-clock < 1 s assertion on a loaded host. The file is untouched here, and it passed 50/50 on rerun.
  • New and updated tests:
    • Prepared prefix within the budget; a mismatched prefix is never reused; lost lease → whole prefix refunded with attempt 0; release when the GPU section ends; a running fetch stays reserved; kill switch (test_batch_pipeline_worker_bundle.py).
    • VAD-score reuse identity, consume-once, weak drop, shape fallback, kill switch, wiring only without flight batching, and exact equality with serial pyannote (test_batch_pipeline_flight.py).
    • Graph or eager mode persisting through NeMo's timestamp rebuild, plus violation quarantine (test_batch_pipeline_runtime.py).
    • Helm kill switches (test_batch_pipeline_chart.py).
  • The pyannote-backed equality tests skip locally (no torch). Run on the dev L4 pod against pyannote 4.0.3 (pytest --noconftest -k pyannote), 9 passed, including test_vad_segmentation_reuse_matches_serial_pyannote_exactly.
  • make preflight: see the CI run.

UI evidence

UI-Evidence: none -- backend GPU worker only

Failure class (fixes)

Failure-Class: none

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

Git-on-my-level and others added 3 commits November 2, 2026 18:19
…GPU worker

Ports three accepted GPU-efficiency hill-climb ticks onto main, each behind a
ship-on kill switch read through ProviderConfig:

- BATCH_PIPELINE_PREPARE_FLIGHT_ENABLED (worker.prepareFlightEnabled): a lane
  waiting for the GPU prepares every leading unit of its flight that fits the
  shared 1800-audio-second decode budget, and the chain returns finished
  preparations when its GPU section ends instead of after publication.
- BATCH_PIPELINE_VAD_SEGMENTATION_REUSE_ENABLED
  (worker.vadSegmentationReuseEnabled): a unit diarized at full length takes the
  raw segmentation scores its own VAD pass computed instead of a second forward.

Dev L4 bench (g2-standard-8, simulated storage IO, prod chart env):
main 70.35 / 70.24 -> this 113.65 / 112.85 audio-h per GPU-h; GPU section
99.9 -> 61.7 s; diarize 19.9 -> 15.9 s. Gate vs the L4 main run: word edit
rate 0.0, speaker agreement 1.0, no lost speech or class changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports lane/gpe-cgraph (explicit, asserted decoding mode; a mismatch in either
direction quarantines the process) and turns it on by default on L4 evidence.
BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED / worker.asrCudaGraphsEnabled: false
restores asserted eager decoding.

Dev L4 paired runs (same tree, eager vs graphs, prod chart env, simulated IO):
ASR stage 13.6 -> 10.1 s (-26%), audio-h per GPU-h 57.1 -> 58.7, word edit rate
0, speaker agreement 1.0, CUDA peak +0.15 GiB. On the full stack the ASR stage
drops 13.5 -> 10.0 s; the bench objective stays ~113 because the L4 bench is
then bound by its simulated artifact IO. The T4 FP32 fallback from the hill
climb is not ported: prod batch runs only on L4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-11-02T12:07:40.989143Z
We couldn’t complete this review. Comment @codex review to try again.
a664f61 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found across 20 files

Confidence score: 3/5

  • batch_pipeline_models.py can fail worker startup when CUDA graphs are disabled because NeMo uses cuda_graphs_mode='no_graphs', not None. Treat no_graphs as disabled.
  • feature-flag-registry.md can incorrectly report Helm-declared batch-worker flags as undeclared, hiding their kill switches. Include the batch-worker chart when generating the registry.
  • The benchmark can produce misleading comparisons: graph capture may be timed, and the recorded config and documentation can disagree with the mode actually run. Warm each graph configuration before timing and keep the receipt and instructions aligned with the selected mode.
  • batch_pipeline_runtime.md overstates the preparation guarantee: dispatch can wait on a decode future while holding the GPU lock. Describe preparation as reducing fetch waits rather than preventing them.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/tests/unit/test_batch_pipeline_runtime.py">

<violation number="1" location="backend/tests/unit/test_batch_pipeline_runtime.py:318">
P2: This startup test does not verify the kill switch it claims to cover: a regression that ignored `provider_config().asr_cuda_graphs` during worker construction would still pass. Exercise startup with the environment set to `false` and assert both the model setting and `worker.words.cuda_graphs` are `False`; also set the enabled value explicitly so ambient deployment variables cannot make the test non-hermetic.</violation>
</file>

<file name="backend/tests/unit/test_batch_pipeline_worker_bundle.py">

<violation number="1" location="backend/tests/unit/test_batch_pipeline_worker_bundle.py:867">
P2: This assertion is timing-dependent because the three prepared decodes run concurrently and `decoded` records completion order. Compare an order-independent collection so the test verifies exactly-once decoding without flaking under scheduler variation.</violation>
</file>

<file name="backend/docs/listen_pusher_pipeline/changes/2026-11-02-batch-gpu-flight-prep-vad-reuse.md">

<violation number="1" location="backend/docs/listen_pusher_pipeline/changes/2026-11-02-batch-gpu-flight-prep-vad-reuse.md:21">
P2: The readback instruction misstates the metric granularity and omits its production gate: `worker_flight` is one observation per GPU flight, not per `fetch_decode` unit, and the histogram is normally disabled in production. Distinguish the per-flight and per-unit series and state that stage profiling must be enabled for this readback.</violation>
</file>

<file name="backend/diarizer/batch_pipeline_runtime.md">

<violation number="1" location="backend/diarizer/batch_pipeline_runtime.md:51">
P2: Preparation overlaps fetches but cannot guarantee that the GPU section never waits: dispatch calls `future.result()` for unfinished prepared decodes while the GPU lock is held. Change this to “reduces fetch waits” or qualify the claim for preparations that finish before dispatch.</violation>
</file>

<file name="backend/docs/feature-flag-registry.md">

<violation number="1" location="backend/docs/feature-flag-registry.md:901">
P2: These flags are declared by the batch-worker Helm deployment, so listing them as undeclared falsely says they run code defaults and hides their Helm kill switches. Update the registry generator to inspect this chart’s template or values wiring, then regenerate the document.</violation>
</file>

<file name="backend/diarizer/batch_pipeline_benchmark.py">

<violation number="1" location="backend/diarizer/batch_pipeline_benchmark.py:64">
P3: The benchmark receipt contradicts itself when the graph kill switch is false: the actual decoder runs eager mode, but each row’s serialized `config.asr_cuda_graphs` remains `True`. Populate the row config with the selected `cuda_graphs` value or omit that field from the per-cell configuration.</violation>

<violation number="2" location="backend/diarizer/batch_pipeline_benchmark.py:64">
P2: Graph-enabled rows include one-time CUDA graph capture in their timings: the only warm-up is a single 30-second batch before the size/context loops, so batch-8/32 and context-2 rows use a new graph during measurement. Warm each row with its actual first batch before starting the timer, or report capture and steady-state timings separately.</violation>

<violation number="3" location="backend/diarizer/batch_pipeline_benchmark.py:76">
P3: The benchmark documentation still describes this script as pinning eager decoding, but the new default runs CUDA-graph decoding. Update the qualification instructions and historical note to state the selected mode, or explicitly set `BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED=false` for the documented eager baseline.</violation>
</file>

<file name="backend/diarizer/batch_pipeline_models.py">

<violation number="1" location="backend/diarizer/batch_pipeline_models.py:92">
P1: The eager kill switch can fail worker startup even though CUDA graphs are disabled: NeMo represents the disabled decoder with `cuda_graphs_mode='no_graphs'`, but this check accepts only `None`. Accept the disabled mode as well while continuing to require `full_graph is None`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

if cuda_graphs:
actual = computer.allow_cuda_graphs is True and mode in _GRAPH_MODES
else:
actual = computer.allow_cuda_graphs is False and mode is None and computer.full_graph is None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The eager kill switch can fail worker startup even though CUDA graphs are disabled: NeMo represents the disabled decoder with cuda_graphs_mode='no_graphs', but this check accepts only None. Accept the disabled mode as well while continuing to require full_graph is None.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/diarizer/batch_pipeline_models.py, line 92:

<comment>The eager kill switch can fail worker startup even though CUDA graphs are disabled: NeMo represents the disabled decoder with `cuda_graphs_mode='no_graphs'`, but this check accepts only `None`. Accept the disabled mode as well while continuing to require `full_graph is None`.</comment>

<file context>
@@ -58,27 +71,38 @@ def pin_parakeet_decoding(model):
+    if cuda_graphs:
+        actual = computer.allow_cuda_graphs is True and mode in _GRAPH_MODES
+    else:
+        actual = computer.allow_cuda_graphs is False and mode is None and computer.full_graph is None
+    if not actual:
         raise RuntimeError('worker CUDA graph policy violated; restart required')
</file context>
Suggested change
actual = computer.allow_cuda_graphs is False and mode is None and computer.full_graph is None
actual = computer.allow_cuda_graphs is False and mode in (None, 'no_graphs') and computer.full_graph is None

assert asr.cfg.decoding.greedy.use_cuda_graph_decoder is False
# Graph decoding ships on; the kill switch restores asserted eager decoding.
assert asr.cfg.decoding.greedy.use_cuda_graph_decoder is True
assert worker.words.cuda_graphs is True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This startup test does not verify the kill switch it claims to cover: a regression that ignored provider_config().asr_cuda_graphs during worker construction would still pass. Exercise startup with the environment set to false and assert both the model setting and worker.words.cuda_graphs are False; also set the enabled value explicitly so ambient deployment variables cannot make the test non-hermetic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/tests/unit/test_batch_pipeline_runtime.py, line 318:

<comment>This startup test does not verify the kill switch it claims to cover: a regression that ignored `provider_config().asr_cuda_graphs` during worker construction would still pass. Exercise startup with the environment set to `false` and assert both the model setting and `worker.words.cuda_graphs` are `False`; also set the enabled value explicitly so ambient deployment variables cannot make the test non-hermetic.</comment>

<file context>
@@ -239,7 +313,9 @@ def test_startup_pins_precision_reuses_segmentation_and_owns_one_gpu(monkeypatch
-    assert asr.cfg.decoding.greedy.use_cuda_graph_decoder is False
+    # Graph decoding ships on; the kill switch restores asserted eager decoding.
+    assert asr.cfg.decoding.greedy.use_cuda_graph_decoder is True
+    assert worker.words.cuda_graphs is True
     fake_torch.cuda.device_count = lambda: 2
     startup_calls = list(calls)
</file context>

finally:
prepared.close()
assert [r.segment_id for r in results] == ['a', 'b', 'c', 'd']
assert decoded == ['a', 'b', 'c', 'd']

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This assertion is timing-dependent because the three prepared decodes run concurrently and decoded records completion order. Compare an order-independent collection so the test verifies exactly-once decoding without flaking under scheduler variation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/tests/unit/test_batch_pipeline_worker_bundle.py, line 867:

<comment>This assertion is timing-dependent because the three prepared decodes run concurrently and `decoded` records completion order. Compare an order-independent collection so the test verifies exactly-once decoding without flaking under scheduler variation.</comment>

<file context>
@@ -850,3 +850,129 @@ def test_strict_vad_shadow_sampling_skips_inspect_and_counters_without_changing_
+    finally:
+        prepared.close()
+    assert [r.segment_id for r in results] == ['a', 'b', 'c', 'd']
+    assert decoded == ['a', 'b', 'c', 'd']
+    assert worker._decode_budget.seconds == 0
+
</file context>
Suggested change
assert decoded == ['a', 'b', 'c', 'd']
assert sorted(decoded) == ['a', 'b', 'c', 'd']

Comment on lines +21 to +22
roll: `omi_batch_pipeline_stage_seconds` for `worker_flight` (GPU section) per
`fetch_decode` unit, and the `diarize` stage mean, against the pre-roll window.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The readback instruction misstates the metric granularity and omits its production gate: worker_flight is one observation per GPU flight, not per fetch_decode unit, and the histogram is normally disabled in production. Distinguish the per-flight and per-unit series and state that stage profiling must be enabled for this readback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/docs/listen_pusher_pipeline/changes/2026-11-02-batch-gpu-flight-prep-vad-reuse.md, line 21:

<comment>The readback instruction misstates the metric granularity and omits its production gate: `worker_flight` is one observation per GPU flight, not per `fetch_decode` unit, and the histogram is normally disabled in production. Distinguish the per-flight and per-unit series and state that stage profiling must be enabled for this readback.</comment>

<file context>
@@ -0,0 +1,24 @@
+  themselves. `false` restores the second forward.
+
+Both are worker-only kill switches, read once at startup. Readback after the
+roll: `omi_batch_pipeline_stage_seconds` for `worker_flight` (GPU section) per
+`fetch_decode` unit, and the `diarize` stage mean, against the pre-roll window.
+See [runtime](../../../diarizer/batch_pipeline_runtime.md) and
</file context>
Suggested change
roll: `omi_batch_pipeline_stage_seconds` for `worker_flight` (GPU section) per
`fetch_decode` unit, and the `diarize` stage mean, against the pre-roll window.
roll (with stage profiling enabled): `omi_batch_pipeline_stage_seconds` for `worker_flight` (one GPU-section observation per flight), `fetch_decode` (one per unit), and the `diarize` stage mean, against the pre-roll window.

before publication, with renewal still active. A pull admits at most 32 units,
exclusive behind one asyncio lock. Each waiting lane prepares the leading
units of its flight that fit one shared 1800-audio-second decode budget, using
the same artifact IO pool (default five), so the GPU section does not wait on

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Preparation overlaps fetches but cannot guarantee that the GPU section never waits: dispatch calls future.result() for unfinished prepared decodes while the GPU lock is held. Change this to “reduces fetch waits” or qualify the claim for preparations that finish before dispatch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/diarizer/batch_pipeline_runtime.md, line 51:

<comment>Preparation overlaps fetches but cannot guarantee that the GPU section never waits: dispatch calls `future.result()` for unfinished prepared decodes while the GPU lock is held. Change this to “reduces fetch waits” or qualify the claim for preparations that finish before dispatch.</comment>

<file context>
@@ -39,10 +46,18 @@ legacy provider clients into the GPU image is unnecessary.
-before publication, with renewal still active. A pull admits at most 32 units,
+exclusive behind one asyncio lock. Each waiting lane prepares the leading
+units of its flight that fit one shared 1800-audio-second decode budget, using
+the same artifact IO pool (default five), so the GPU section does not wait on
+fetches (`BATCH_PIPELINE_PREPARE_FLIGHT_ENABLED`, Helm
+`worker.prepareFlightEnabled`, default on; `false` prepares only the first
</file context>
Suggested change
the same artifact IO pool (default five), so the GPU section does not wait on
the same artifact IO pool (default five), so the GPU section usually avoids waiting on

- `BATCH_CONVERSATION_SPEAKERS_ENABLED` — Default on within persisted authoritative batch records; link unit speaker groups acoustically and name conversation groups (fail: open)
- `BATCH_JOBSTORE_MODE` — Default firestore; shadow mirrors committed batch job-store writes to the staging Postgres and counts read parity (Firestore stays the only authority) (fail: closed)
- `BATCH_LEGACY_CAPACITY_FALLBACK_ENABLED` — Default on; while the GPU ring reads saturated (see BATCH_VENDOR_OVERFLOW_MIN_READY_UNITS), a new fresh upload from an owner whose legacy sync is still on stays on legacy sync instead of queueing behind the ring. false queues it on batch as before (fail: open)
- `BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED` — Default on unless false; batch worker Parakeet TDT greedy_batch label-looping decoding runs NeMo CUDA graphs instead of eager kernels (same algorithm, identical words); the chosen mode is asserted before and after every transcription and a mismatch quarantines the process (fail: open)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These flags are declared by the batch-worker Helm deployment, so listing them as undeclared falsely says they run code defaults and hides their Helm kill switches. Update the registry generator to inspect this chart’s template or values wiring, then regenerate the document.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/docs/feature-flag-registry.md, line 901:

<comment>These flags are declared by the batch-worker Helm deployment, so listing them as undeclared falsely says they run code defaults and hides their Helm kill switches. Update the registry generator to inspect this chart’s template or values wiring, then regenerate the document.</comment>

<file context>
@@ -895,6 +898,7 @@ their code default (`fail` tells you which way a missing value resolves).
 - `BATCH_CONVERSATION_SPEAKERS_ENABLED` — Default on within persisted authoritative batch records; link unit speaker groups acoustically and name conversation groups (fail: open)
 - `BATCH_JOBSTORE_MODE` — Default firestore; shadow mirrors committed batch job-store writes to the staging Postgres and counts read parity (Firestore stays the only authority) (fail: closed)
 - `BATCH_LEGACY_CAPACITY_FALLBACK_ENABLED` — Default on; while the GPU ring reads saturated (see BATCH_VENDOR_OVERFLOW_MIN_READY_UNITS), a new fresh upload from an owner whose legacy sync is still on stays on legacy sync instead of queueing behind the ring. false queues it on batch as before (fail: open)
+- `BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED` — Default on unless false; batch worker Parakeet TDT greedy_batch label-looping decoding runs NeMo CUDA graphs instead of eager kernels (same algorithm, identical words); the chosen mode is asserted before and after every transcription and a mismatch quarantines the process (fail: open)
 - `BATCH_PIPELINE_ASR_LEFT_CONTEXT_SECONDS` — Default 0 (off); seconds of the same capture stream's contiguous predecessor speech run prefixed to a unit whose speech starts at sample 0, so Parakeet TDT does not start mid-utterance (fail: closed)
 - `BATCH_PIPELINE_ASR_LEFT_CONTEXT_UNITS` — Process-local predecessor tails kept for ASR left context (default 64, range [0, 1024]; each <=8 s int16, 10 min TTL, keyed by owner, privacy generation and capture stream: source, device and lock from the staged intake context, or the record conversation) (fail: closed)
</file context>

model = nemo_asr.models.ASRModel.from_pretrained(args.model).cuda().eval()
pin_parakeet_decoding(model)
provider = ParakeetWords(model, enforce_eager=True)
cuda_graphs = provider_config().asr_cuda_graphs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Graph-enabled rows include one-time CUDA graph capture in their timings: the only warm-up is a single 30-second batch before the size/context loops, so batch-8/32 and context-2 rows use a new graph during measurement. Warm each row with its actual first batch before starting the timer, or report capture and steady-state timings separately.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/diarizer/batch_pipeline_benchmark.py, line 64:

<comment>Graph-enabled rows include one-time CUDA graph capture in their timings: the only warm-up is a single 30-second batch before the size/context loops, so batch-8/32 and context-2 rows use a new graph during measurement. Warm each row with its actual first batch before starting the timer, or report capture and steady-state timings separately.</comment>

<file context>
@@ -61,8 +61,9 @@ def main():
     model = nemo_asr.models.ASRModel.from_pretrained(args.model).cuda().eval()
-    pin_parakeet_decoding(model)
-    provider = ParakeetWords(model, enforce_eager=True)
+    cuda_graphs = provider_config().asr_cuda_graphs
+    pin_parakeet_decoding(model, cuda_graphs=cuda_graphs)
+    provider = ParakeetWords(model, cuda_graphs=cuda_graphs)
</file context>

'torch': torch.__version__,
'model': args.model,
'cuda_graph_policy': 'persistently_disabled_and_asserted',
'cuda_graph_policy': 'enabled_and_asserted' if cuda_graphs else 'persistently_disabled_and_asserted',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The benchmark documentation still describes this script as pinning eager decoding, but the new default runs CUDA-graph decoding. Update the qualification instructions and historical note to state the selected mode, or explicitly set BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED=false for the documented eager baseline.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/diarizer/batch_pipeline_benchmark.py, line 76:

<comment>The benchmark documentation still describes this script as pinning eager decoding, but the new default runs CUDA-graph decoding. Update the qualification instructions and historical note to state the selected mode, or explicitly set `BATCH_PIPELINE_ASR_CUDA_GRAPHS_ENABLED=false` for the documented eager baseline.</comment>

<file context>
@@ -72,7 +73,7 @@ def main():
         'torch': torch.__version__,
         'model': args.model,
-        'cuda_graph_policy': 'persistently_disabled_and_asserted',
+        'cuda_graph_policy': 'enabled_and_asserted' if cuda_graphs else 'persistently_disabled_and_asserted',
         'rows': rows,
         'scope': 'ASR only; whole conversation input, internal windows; no persistence',
</file context>

model = nemo_asr.models.ASRModel.from_pretrained(args.model).cuda().eval()
pin_parakeet_decoding(model)
provider = ParakeetWords(model, enforce_eager=True)
cuda_graphs = provider_config().asr_cuda_graphs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The benchmark receipt contradicts itself when the graph kill switch is false: the actual decoder runs eager mode, but each row’s serialized config.asr_cuda_graphs remains True. Populate the row config with the selected cuda_graphs value or omit that field from the per-cell configuration.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At backend/diarizer/batch_pipeline_benchmark.py, line 64:

<comment>The benchmark receipt contradicts itself when the graph kill switch is false: the actual decoder runs eager mode, but each row’s serialized `config.asr_cuda_graphs` remains `True`. Populate the row config with the selected `cuda_graphs` value or omit that field from the per-cell configuration.</comment>

<file context>
@@ -61,8 +61,9 @@ def main():
     model = nemo_asr.models.ASRModel.from_pretrained(args.model).cuda().eval()
-    pin_parakeet_decoding(model)
-    provider = ParakeetWords(model, enforce_eager=True)
+    cuda_graphs = provider_config().asr_cuda_graphs
+    pin_parakeet_decoding(model, cuda_graphs=cuda_graphs)
+    provider = ParakeetWords(model, cuda_graphs=cuda_graphs)
</file context>

@Git-on-my-level
Git-on-my-level merged commit 1863dec into main Nov 2, 2026
68 of 70 checks passed
@Git-on-my-level
Git-on-my-level deleted the lane/gpe-port branch November 2, 2026 16:15
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