libdemac: reciprocal table for the pivot division on ARMv5+

Of the two divisions a sample left in the entropy decoder, the first
divides the range by the Rice pivot, which is small: below 1024 for
99.9% of the samples of a 16-bit test file and never as much as 4096.
Keep a table of reciprocals, filled in as divisors turn up, and
divide with a 32x32->64 multiply and one correction.  Larger divisors
go to the division routine as before.  The other division, by help,
has no such pattern.

This is for ARMv5 and later without a hardware divide.  ARMv4 has
its own divider with a reciprocal table already, and a long multiply
is slow there; its codec is unchanged.

The table is 16 KB of bss.  With r = (2^32 - 1) / n the estimate is
the quotient or one less for any 32-bit numerator, checked against
true division for every n below 4096.

MHz for real time in perfsim's model of the Clip+, -c1000, -c2000
and -c3000: 32.5, 48.4, 76.4 to 30.1, 46.0, 74.0.  A Clip+ measures
30.63 at -c1000, from 32.85, with the same checksum, 1008ffab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I555ff83d7e19919848c8fb7b8d12ecfa671a976b
This commit is contained in:
Michael Giacomelli 2026-10-05 01:25:09 -04:00 • committed by Solomon Peachy
parent aa7ebbe057
commit a6b7321d9f

View file

@ -184,6 +184,37 @@ DEMAC_INLINE void range_dec_normalize(struct rangecoder_t* rc)
}
}
/* range / tot_f. tot_f is the Rice pivot, which is small (below 1024
for nearly every sample of ordinary 16-bit audio), so where a 32x32->64
multiply is cheap its reciprocal comes from a table that is filled in as
divisors turn up, and the division is a multiply and a correction. The
other division, by help, has no such pattern. */
#if defined(CPU_ARM) && ARM_ARCH >= 5 && !defined(ARM_HAVE_HW_DIV)
#define RECIP_ENTRIES 4096
/* recip[n] is (2^32 - 1) / n, which is never 0: 0 is "not worked out" */
static uint32_t recip[RECIP_ENTRIES];
DEMAC_INLINE uint32_t range_div(uint32_t range, uint32_t tot_f)
{
uint32_t r, q;
if (UNLIKELY(tot_f >= RECIP_ENTRIES))
return UDIV32(range, tot_f);
r = recip[tot_f];
if (UNLIKELY(r == 0))
r = recip[tot_f] = UDIV32(0xffffffff, tot_f);
/* this is the quotient or one less */
q = ((uint64_t)range * r) >> 32;
if (range - q * tot_f >= tot_f)
q++;
return q;
}
#else
#define range_div(range, tot_f) UDIV32(range, tot_f)
#endif
/* Calculate culmulative frequency for next symbol. Does NO update!*/
/* tot_f is the total frequency */
/* or: totf is (code_value)1<<shift */
@ -191,7 +222,7 @@ DEMAC_INLINE void range_dec_normalize(struct rangecoder_t* rc)
DEMAC_INLINE int range_decode_culfreq(struct rangecoder_t* rc, int tot_f)
{
range_dec_normalize(rc);
rc->help = UDIV32(rc->range, tot_f);
rc->help = range_div(rc->range, tot_f);
return UDIV32(rc->low, rc->help);
}