perf: optimize BatchedInferencePipeline — VAD on GPU, feature pipelining, buffer pre-allocation - #1452
Open
RozhinKh wants to merge 1 commit into
Open
perf: optimize BatchedInferencePipeline — VAD on GPU, feature pipelining, buffer pre-allocation#1452RozhinKh wants to merge 1 commit into
RozhinKh wants to merge 1 commit into
Conversation
…ing, buffer pre-allocation Profiling showed CPU preprocessing was the dominant bottleneck in BatchedInferencePipeline on modern GPUs. This commit addresses it with six targeted changes to transcribe.py and vad.py. faster_whisper/vad.py: - SileroVADModel: prefer CUDAExecutionProvider over hardcoded CPU; falls back to CPU when CUDA unavailable. Reduces VAD time from ~1.5 s to ~0.05 s per request on GPU hardware. - SileroVADModel.__call__: single np.empty allocation replaces the separate np.pad copy the caller previously had to perform. Zeroes only segment-0 context strip and last-segment tail — avoids ~80 MB memset for 21-minute audio. Output verified bit-identical to the original np.pad approach across 11 audio lengths (0 to 20M samples). faster_whisper/transcribe.py: - Feature extraction pipelining: ThreadPoolExecutor(max_workers=1) submits all batch extraction futures upfront so a background thread extracts batch N+1 features while CTranslate2 processes batch N on GPU. CTranslate2 releases the GIL during CUDA ops so the thread runs freely with no extra synchronization. - Feature buffer pre-allocation: np.zeros((B, n_mels, 3000)) replaces np.stack([pad_or_trim(f) for f in feats]), eliminating two intermediate allocations per batch. - Single-batch executor skip: when len(batch_starts)==1 the executor is bypassed entirely — no pipeline benefit exists and thread creation overhead is pure cost for short audio. - Eliminated duplicate tokenizer decode: walrus operator ensures tokenizer.decode(tokens) is called once per subsegment instead of twice (text field + get_compression_ratio). Results (cold-pass, cache disabled, same config on both branches): RTX 3090 int8_float16/beam=5/batch=32 -15.7 to -21.6% +27.6% throughput A100-SXM4 bfloat16/beam=5/batch=32 -18.2 to -22.9% across 9 scenarios SHA1-identical outputs. 6/6 noise conditions pass 3% WER threshold.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-to-end latency improvements for
BatchedInferencePipelinevia CPU pipeline restructuring and GPU placement fixes. Config held constant on both branches — code changes only, no model or decoder modifications.Results (cold-pass, cache disabled, same config on both branches):
SHA1-identical outputs across runs. 6/6 noise robustness conditions pass 3% WER regression threshold. All 9 Artemis ASR scenarios pass validity gate.
Changes
faster_whisper/vad.pyVAD on GPU —
SileroVADModelpreviously hardcodedCPUExecutionProvider. Profiling showed the 1 MB VAD model was consuming ~1.5 s per request due to sequential CPU execution across hundreds of 512-sample windows. The session now prefersCUDAExecutionProvider(falling back to CPU when unavailable), reducing VAD time to ~0.05 s per request.Single-allocation VAD buffer —
__call__previously required an already-padded array (a fullnp.padcopy). The new implementation accepts raw audio of any length and constructs the segmented buffer in a singlenp.emptyallocation, zeroing only the segment-0 context strip and the last-segment tail padding. Eliminates ~80 MB of data movement for 21-minute audio. Verified bit-identical to the original across 11 audio lengths (0 to 20,160,001 samples).faster_whisper/transcribe.pyFeature extraction pipelining —
BatchedInferencePipelinepreviously blocked GPU inference waiting for CPU feature extraction to finish. AThreadPoolExecutor(max_workers=1)now submits all batch extraction futures upfront. A background thread extracts batch N+1 features while CTranslate2 processes batch N on the GPU. CTranslate2 releases the GIL during CUDA operations, so the background thread runs freely with no synchronization overhead.Feature buffer pre-allocation — replaced
np.stack([pad_or_trim(f) for f in feats])with a pre-allocatednp.zeros((batch_size, n_mels, 3000), dtype=np.float32)result buffer, eliminating two intermediate allocations per batch.Single-batch executor skip — for audio that fits in a single batch, bypasses the executor entirely since there is nothing to pipeline. Avoids thread creation overhead for short audio without affecting multi-batch workloads.
Eliminated duplicate tokenizer decode —
tokenizer.decode(tokens)was called twice per subsegment inforward(). Walrus operator reduces this to one call.Test plan
benchmark/speed_benchmark.pyon baseline vs optimized at matching config