From c9797167f58bbd5379c8e523d903cb65a3bf8ed9 Mon Sep 17 00:00:00 2001 From: parthvelobyte Date: Sat, 5 Sep 2026 14:46:12 -0700 Subject: [PATCH] bits: replace per-probe multiplies with one division in lc3_get_symbol The binary search over the ac model compares ac->low against range * symbols[s].low at every probe, which puts a data-dependent multiply on the decoder's critical path for every symbol read. For unsigned integers with range >= 1, low < range * L <=> low / range < L (range*q <= low < range*(q+1), so low < range*L iff q < L), so the quotient computed once turns every probe into a plain table compare. The final low/range update is unchanged. Encoded and decoded outputs are byte-identical before and after the change across a 180 s corpus at 32/96 kbps, 7.5 and 10 ms frames. Decoder wall time measures 3-5% faster on Apple M2 Pro (clang 15, -O3 -ffast-math, interleaved A/B, median of 8 rounds). --- src/bits.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/bits.h b/src/bits.h index de65c93..2a9502d 100644 --- a/src/bits.h +++ b/src/bits.h @@ -289,12 +289,20 @@ LC3_HOT static inline unsigned lc3_get_symbol( int s = 16; + /* Each probe of the binary search would chain a data-dependent + * multiply on the critical path. For the decoder's unsigned values + * (low < 2^24, and 0x40 <= range <= 0x3fff from the renormalization + * invariant ac->range >= 0x10000), + * low < range * symbols[s].low <=> low / range < symbols[s].low + * so the quotient, computed once, decides every probe instead. */ + if (ac->low < range * symbols[s].low) { + unsigned q = ac->low / range; s >>= 1; - s -= ac->low < range * symbols[s].low ? 4 : -4; - s -= ac->low < range * symbols[s].low ? 2 : -2; - s -= ac->low < range * symbols[s].low ? 1 : -1; - s -= ac->low < range * symbols[s].low; + s -= q < symbols[s].low ? 4 : -4; + s -= q < symbols[s].low ? 2 : -2; + s -= q < symbols[s].low ? 1 : -1; + s -= q < symbols[s].low; } ac->low -= range * symbols[s].low;