Skip to content

Parakeet-TDT NPU acceleration on the Apple Neural Engine - #729

Merged
HenryNdubuaku merged 4 commits into
mainfrom
parakeet-npu-ane
Jun 16, 2026
Merged

HenryNdubuaku merged 4 commits into
mainfrom
parakeet-npu-ane

Conversation

@ncylich

@ncylich ncylich commented Jun 16, 2026 •

Copy link
Copy Markdown
Collaborator

Benchmarks

Records the Mac M4 Pro and Galaxy S26 README numbers (consolidates #725), with Parakeet-TDT now transcribing on the Apple Neural Engine on Mac.

  • LLM: Gemma-4-E2B-CQ4 (CPU, no speculative decode) — 1k-prefill tps / 100-decode tps
  • VLM: Gemma-4-E2B-CQ4 (NPU prefill, CPU decode), 256px — latency / decode tps
  • Transcribe: Parakeet-TDT-0.6B (NPU encoder, CPU decode), 20s audio — latency
Device Gemma4 Text Gemma4 Vision Parakeet RAM
Mac M4 Pro 324 / 39 1.2s / 48 0.2s (NPU) 1385 MB
Galaxy S26 248 / 21 17s / 16 4.2s 4024 MB

On the M4 Pro the Parakeet 20s clip drops from ~1.2s (CPU encoder) to ~0.2s on the Neural Engine — ~6× faster — with the CPU path unchanged and the transcript matching CPU.

Parakeet NPU acceleration

Adds Apple Neural Engine (ANE) acceleration for the Parakeet-TDT audio encoder.

ANE-friendly encoder export. The Conformer relative-position attention used a torch.gather (plus a runtime reverse) that the ANE compiler rejects. It's replaced with a static Transformer-XL rel-shift over precomputed, reversed position keys — numerically identical to the gather — captured at a fixed sequence length as a separate npu_module. A new npu_example_inputs spec field lets the NPU export shape be set independently of the CPU graph. The torch.export _assert_tensor_metadata node, which coremltools can't translate, is registered as a no-op.

Padding mask (transcript fix). The encoder exports at a fixed window and the engine zero-pads shorter clips, but the Conformer's global attention had no padding mask, so the padded frames leaked into every real frame across all layers and drifted the transcript. The ANE encoder now derives the exact valid-frame count from the input (real mel frames are non-zero, padding is exact zero) and applies it as an additive attention key bias plus a depthwise-conv mask, matching the unpadded CPU encoder (cos 0.9997 vs 0.959 before). What previously looked like fp16 "drift" was unmasked padding — not precision or quantization.

Compute-units routing. The bundle manifest records the audio encoder's CoreML compute units (npu_audio_compute_units), threaded through to the encoder loader and honored when resolving compute units. The transpiler writes CPU_AND_NE for parakeet_tdt; the default is empty, so other encoders are unaffected. Audio longer than one NPU window falls back to the CPU encoder to preserve global attention.

Notes.

  • Verified end to end through the engine STT path: the bundle auto-routes to the Neural Engine (no env var) and transcribes correctly, matching the CPU transcript.
  • The rel-shift was verified equal to the original gather (max diff 0.0) across sequence lengths; the CPU graph/decoder path is unchanged.
  • A MILCompilerForANE … ANECCompile() FAILED log line is emitted but is non-fatal: one sub-region falls back while the bulk runs on the ANE. Cold ANE compilation of the 24-layer Conformer is slow and variable (Apple-compiler-side, independent of the mask); it is cached as .mlmodelc after the first load.

ncylich added 4 commits June 15, 2026 20:00
Export a fixed-shape Parakeet-TDT Conformer audio encoder the Apple
Neural Engine can compile: replace the relative-position torch.gather
with a static Transformer-XL rel-shift over precomputed reversed
position keys, registered as a separate npu_module captured at a fixed
sequence length via a new npu_example_inputs spec field. No-op the
torch.export _assert_tensor_metadata node coremltools cannot translate.

Signed-off-by: Noah Cylich <noahcylich@gmail.com>
Record an optional npu_audio_compute_units hint in the bundle manifest
and honor it when loading the NPU audio encoder, so the Parakeet encoder
runs on the Neural Engine while other audio encoders keep their default.
The transpiler writes CPU_AND_NE for parakeet_tdt; only audio longer
than one NPU window falls back to the CPU encoder.

Signed-off-by: Noah Cylich <noahcylich@gmail.com>
The fixed-shape NPU encoder zero-pads short clips to its window, but the
Conformer's global attention had no padding mask, so the padded frames
leaked into every real frame across all layers and drifted the transcript.
Derive the exact valid frame count from the input and apply it as an
additive attention key bias and a depthwise-conv mask, matching the
unpadded CPU encoder (cos 0.9997 vs 0.959 before).

Signed-off-by: Noah Cylich <noahcylich@gmail.com>
Parakeet-TDT transcribes the 20s sample in 0.2s on the Mac Neural Engine,
~6x faster than the 1.2s CPU fallback; also records the Gemma4 text/vision
and Galaxy S26 numbers.

Signed-off-by: Noah Cylich <noahcylich@gmail.com>
@ncylich
ncylich force-pushed the parakeet-npu-ane branch from 10ad94c to 30c229f Compare June 16, 2026 08:13
@HenryNdubuaku
HenryNdubuaku merged commit 09147fc into main Jun 16, 2026
5 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.

2 participants