Skip to content

Optimize Opus decoding for ARM64 - #239

Merged
zshang-oai merged 1 commit into
pion:mainfrom
zshang-oai:codex/opus-decoder-perf-20260901
Sep 3, 2026
Merged

zshang-oai merged 1 commit into
pion:mainfrom
zshang-oai:codex/opus-decoder-perf-20260901

Conversation

@zshang-oai

@zshang-oai zshang-oai commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Reduce decoder CPU time with portable specializations and an ARM64 NEON resampling kernel. On an Apple M4 Max, complete RFC stream replays with 24 kHz mono DecodeToInt16 output take 18.5–27.0% less time than the base revision.

Implementation

  • Avoid integer divisions when decoding symbols from total-256 probability tables, with the generic path retained for other totals and range states.
  • Specialize CELT deemphasis for mono 48 kHz and 24 kHz output while preserving filter updates for discarded samples.
  • Select SILK excitation sign contexts once per 16-sample block and apply pseudorandom sign inversion with integer masking.
  • Specialize 10/16-tap LPC synthesis and separate the first subframe's history-dependent warm-up from its contiguous tail.
  • Partition LTP rewhitening and pitch prediction into contiguous history spans and boundary-crossing spans. Normalize coefficients once per subframe and use constant tap indices within contiguous spans.
  • Keep up2HQ filter state in locals, simplify equivalent fixed-point multiplication, and add an eight-tap NEON FIR kernel. Assembly is selected by arm64 && go1.27 && !purego; other builds use the scalar implementation. The shared PCM quantizer is unchanged.

LTP correctness

Frame index zero separates saved history from the current frame. Rewhitening an output at i reads [i-dLPC, i], so only 0 <= i < dLPC needs mixed history access. Five-tap pitch prediction reads [i-lag-2, i-lag+2], so only lag-2 <= i < lag+2 needs mixed access: at most four output samples.

For a contiguous output span [A, B), the pitch helper receives source [A-lag-2, B-lag+2). Its local tap source[t+4-k] maps exactly to the original index A+t-lag+2-k. Rewhitening uses the corresponding dLPC preceding samples. The helpers preserve tap order, clamp-before-gain scaling, and ascending sample writes. Source and destination may overlap during pitch prediction, so each result is stored before the next output reads its history. The existing gain-ratio adjustment and frame-history handoff are preserved.

Measurements

Apple M4 Max, Go 1.27.0, 24 kHz mono DecodeToInt16; values are median microseconds per packet. Each implementation runs five rounds of 60 complete-stream replays, with rotated execution order, a fresh decoder per replay, and warm-up outside the timer. Reference libopus is the optimized Homebrew 1.6.1 build; its decoding loop runs in C without per-packet cgo calls.

Official stream Base 1e54f79 Portable + NEON libopus
12: mono, 20 ms 13.43 9.87 6.79
04: WB SILK 17.77 12.97 9.36
05: SWB hybrid 21.70 17.33 10.63
06: FB hybrid 24.15 19.69 12.08

Timings were collected before comment cleanup and equivalent loop rewrites for lint; the final cleanup has not been retimed. These are complete conformance-stream measurements on this host. The reference integer API uses soft clipping while Pion's integer API hard-clips. Ratios remain 1.39–1.63 times libopus; results do not establish performance on other ARM64 CPUs.

Validation

  • Full repository TestRFC6716Conformance: 120/120 passed, covering all 12 streams, five sample rates, and mono/stereo. Latest ARM64 run after the lint fixes: Go 1.27.1, 73.189 s, no skips or failures, unchanged quality matrix.
  • The full corpus also passed on the ARM64 purego path. NEON and portable output hashes matched for all 120 cases in the retained implementation.
  • Linux/amd64 scalar fallback cross-build passed; git diff --check is clean.
  • LPC unrolling can change compiler floating-point fusion. The cumulative change passes conformance but is not bit-identical to the base revision.

With the repository's RFC reference source and test vectors prepared:

OPUS_RFC6716_REFERENCE="$REFERENCE" \
OPUS_RFC6716_TESTVECTORS="$TESTVECTORS" \
go test -count=1 -timeout 20m -tags conformance \
  -run '^TestRFC6716Conformance$' -parallel 4 -v .

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.13725% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.34%. Comparing base (1e54f79) to head (7d06453).

Files with missing lines Patch % Lines
internal/silk/lpc_synthesis.go 92.30% 7 Missing ⚠️
internal/silk/ltp_synthesis.go 89.28% 6 Missing ⚠️
internal/celt/synthesis.go 84.61% 3 Missing and 1 partial ⚠️
internal/silk/decoder.go 95.40% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #239      +/-   ##
==========================================
- Coverage   93.37%   93.34%   -0.03%     
==========================================
  Files          58       61       +3     
  Lines       10710    10928     +218     
==========================================
+ Hits        10000    10201     +201     
- Misses        501      518      +17     
  Partials      209      209              
Flag Coverage Δ
go 93.34% <93.13%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Specialize entropy decoding, SILK synthesis, CELT deemphasis, and
fixed-point resampling. Use an ARM64 NEON kernel for the eight-tap FIR
interpolator, with portable fallbacks. Document the arithmetic
constraints and prediction history boundaries.

Validation: all 120 official RFC conformance cases pass on Go 1.27.1
ARM64 with an unchanged quality matrix. Build selection retains NEON on
ARM64 and the scalar fallback on other architectures; the Linux amd64
build passes.
@zshang-oai
zshang-oai force-pushed the codex/opus-decoder-perf-20260901 branch from 60d2e20 to 7d06453 Compare September 2, 2026 21:53
@zshang-oai
zshang-oai marked this pull request as ready for review September 2, 2026 21:55

@JoTurk JoTurk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

last time I read ARM64 assembly or NEON SIMD I was still in uni years ago .. so I can't really review this I just hope it's correct and doesn't have bugs. I tested it yesterday and it seem able to compile and run.

@zshang-oai
zshang-oai merged commit 8d48e4f into pion:main Sep 3, 2026
20 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