From aa7ebbe057e2c6fef139fa032008edba181dfb9e Mon Sep 17 00:00:00 2001 From: Michael Giacomelli Date: Mon, 5 Oct 2026 01:00:12 -0400 Subject: [PATCH] libdemac: entropy state in registers, one less divide After the division routines the entropy decoder was the largest part of Monkey's Audio at the fast levels, with two things to gain. All of its state, the range coder's and the Rice parameters, was in statics, so each use was a load and each update a store: about half of the function's time on ARM7TDMI, where a load is 3 cycles. Copy it to locals for the length of a block and write it back after. The functions that take a pointer to it have to be inlined for that to work, and on ARMv4 gcc left the per-sample one out of line, so force them. The symbol was found by dividing low by help and searching the count table for the quotient. counts[n] <= low / help is the same as counts[n] * help <= low, so search with the multiply instead: the first symbols are by far the likeliest. That leaves two divisions a sample from three. MHz for real time in perfsim's models, -c1000, -c2000 and -c3000: Clip+ (ARMv5) 37.9, 53.8, 81.8 to 32.5, 48.4, 76.4 e200 (ARMv4) 45.3, 68.6, 111.4 to 35.2, 58.5, 101.3 A Clip+ measures 32.85 at -c1000 (61.4 before this and the division change), with test_codec's checksum, 1008ffab, the same as the old code's. The standalone decoder gives identical output for all four levels tested. The path for files older than 3.98 has the same change and was not tested, for want of a file. Co-Authored-By: Claude Opus 5.5 Change-Id: Idf9002a5b5f3a423f44f70443936ea5ac427cd12 --- lib/rbcodec/codecs/demac/libdemac/entropy.c | 239 ++++++++++---------- 1 file changed, 122 insertions(+), 117 deletions(-) diff --git a/lib/rbcodec/codecs/demac/libdemac/entropy.c b/lib/rbcodec/codecs/demac/libdemac/entropy.c index 44b460fe9f..347bff37eb 100644 --- a/lib/rbcodec/codecs/demac/libdemac/entropy.c +++ b/lib/rbcodec/codecs/demac/libdemac/entropy.c @@ -121,21 +121,37 @@ each function (and the RNGC macro)). for aligned reads. */ -static unsigned char* bytebuffer IBSS_ATTR_DEMAC; -static int bytebufferoffset IBSS_ATTR_DEMAC; - -static inline void skip_byte(void) +/* The decoder's state. It lives in statics between calls, but while a + block is decoded it is copied to locals, so that the compiler can keep + it in registers: with every field in memory, loads and stores were about + half of the decode time on ARM7TDMI. That only works if everything + that takes a pointer to it is inlined, which the compiler does not + always choose to do by itself. */ +#define DEMAC_INLINE static inline __attribute__((always_inline)) +struct rangecoder_t { - bytebufferoffset--; - bytebuffer += bytebufferoffset & 4; - bytebufferoffset &= 3; + uint32_t low; /* low end of interval */ + uint32_t range; /* length of interval */ + uint32_t help; /* bytes_to_follow resp. intermediate value */ + unsigned int buffer; /* buffer for input/output */ + unsigned char* bytebuffer; + int bytebufferoffset; +}; + +static struct rangecoder_t rc IBSS_ATTR_DEMAC; + +DEMAC_INLINE void skip_byte(struct rangecoder_t* rc) +{ + rc->bytebufferoffset--; + rc->bytebuffer += rc->bytebufferoffset & 4; + rc->bytebufferoffset &= 3; } -static inline int read_byte(void) +DEMAC_INLINE int read_byte(struct rangecoder_t* rc) { - int ch = bytebuffer[bytebufferoffset]; + int ch = rc->bytebuffer[rc->bytebufferoffset]; - skip_byte(); + skip_byte(rc); return ch; } @@ -150,31 +166,21 @@ static inline int read_byte(void) #define EXTRA_BITS ((CODE_BITS-2) % 8 + 1) #define BOTTOM_VALUE (TOP_VALUE >> 8) -struct rangecoder_t -{ - uint32_t low; /* low end of interval */ - uint32_t range; /* length of interval */ - uint32_t help; /* bytes_to_follow resp. intermediate value */ - unsigned int buffer; /* buffer for input/output */ -}; - -static struct rangecoder_t rc IBSS_ATTR_DEMAC; - /* Start the decoder */ -static inline void range_start_decoding(void) +DEMAC_INLINE void range_start_decoding(struct rangecoder_t* rc) { - rc.buffer = read_byte(); - rc.low = rc.buffer >> (8 - EXTRA_BITS); - rc.range = (uint32_t) 1 << EXTRA_BITS; + rc->buffer = read_byte(rc); + rc->low = rc->buffer >> (8 - EXTRA_BITS); + rc->range = (uint32_t) 1 << EXTRA_BITS; } -static inline void range_dec_normalize(void) +DEMAC_INLINE void range_dec_normalize(struct rangecoder_t* rc) { - while (rc.range <= BOTTOM_VALUE) - { - rc.buffer = (rc.buffer << 8) | read_byte(); - rc.low = (rc.low << 8) | ((rc.buffer >> 1) & 0xff); - rc.range <<= 8; + while (rc->range <= BOTTOM_VALUE) + { + rc->buffer = (rc->buffer << 8) | read_byte(rc); + rc->low = (rc->low << 8) | ((rc->buffer >> 1) & 0xff); + rc->range <<= 8; } } @@ -182,87 +188,76 @@ static inline void range_dec_normalize(void) /* tot_f is the total frequency */ /* or: totf is (code_value)1<help = UDIV32(rc->range, tot_f); + return UDIV32(rc->low, rc->help); } -static inline int range_decode_culshift(int shift) +DEMAC_INLINE int range_decode_culshift(struct rangecoder_t* rc, int shift) { - range_dec_normalize(); - rc.help = rc.range >> shift; - return UDIV32(rc.low, rc.help); + range_dec_normalize(rc); + rc->help = rc->range >> shift; + return UDIV32(rc->low, rc->help); } /* Update decoding state */ /* sy_f is the interval length (frequency of the symbol) */ /* lt_f is the lower end (frequency sum of < symbols) */ -static inline void range_decode_update(int sy_f, int lt_f) +DEMAC_INLINE void range_decode_update(struct rangecoder_t* rc, + int sy_f, int lt_f) { - rc.low -= rc.help * lt_f; - rc.range = rc.help * sy_f; + rc->low -= rc->help * lt_f; + rc->range = rc->help * sy_f; } /* Decode a byte/short without modelling */ -static inline unsigned char decode_byte(void) -{ int tmp = range_decode_culshift(8); - range_decode_update( 1,tmp); - return tmp; -} - -static inline unsigned short range_decode_short(void) -{ int tmp = range_decode_culshift(16); - range_decode_update( 1,tmp); +DEMAC_INLINE unsigned short range_decode_short(struct rangecoder_t* rc) +{ int tmp = range_decode_culshift(rc, 16); + range_decode_update(rc, 1, tmp); return tmp; } /* Decode n bits (n <= 16) without modelling - based on range_decode_short */ -static inline int range_decode_bits(int n) -{ int tmp = range_decode_culshift(n); - range_decode_update( 1,tmp); +DEMAC_INLINE int range_decode_bits(struct rangecoder_t* rc, int n) +{ int tmp = range_decode_culshift(rc, n); + range_decode_update(rc, 1, tmp); return tmp; } /* Finish decoding */ -static inline void range_done_decoding(void) -{ range_dec_normalize(); /* normalize to use up all bytes */ +DEMAC_INLINE void range_done_decoding(struct rangecoder_t* rc) +{ range_dec_normalize(rc); /* normalize to use up all bytes */ } /* - range_get_symbol_* functions based on main decoding loop in simple_d.c from + range_get_symbol() is based on the main decoding loop in simple_d.c from http://www.compressconsult.com/rangecoder/rngcod13.zip (c) Michael Schindler + + That divides low by help for the cumulative frequency cf and then looks + for the last symbol whose count is not above it. counts[n] <= cf is the + same as counts[n] * help <= low, so the division is not needed: the search + multiplies instead. The first few symbols are by far the likeliest, so + it is short. No count is above 65536 and help is below it here, so the + product cannot overflow. */ - -static inline int range_get_symbol_3980(void) +DEMAC_INLINE int range_get_symbol(struct rangecoder_t* rc, + const int* counts, const int* counts_diff) { - int symbol, cf; + int symbol; - cf = range_decode_culshift(16); + range_dec_normalize(rc); + rc->help = rc->range >> 16; - /* figure out the symbol inefficiently; a binary search would be much better */ - for (symbol = 0; counts_3980[symbol+1] <= cf; symbol++); + for (symbol = 0; (uint32_t)counts[symbol+1] * rc->help <= rc->low; + symbol++); - range_decode_update(counts_diff_3980[symbol],counts_3980[symbol]); - - return symbol; -} - -static inline int range_get_symbol_3970(void) -{ - int symbol, cf; - - cf = range_decode_culshift(16); - - /* figure out the symbol inefficiently; a binary search would be much better */ - for (symbol = 0; counts_3970[symbol+1] <= cf; symbol++); - - range_decode_update(counts_diff_3970[symbol],counts_3970[symbol]); + range_decode_update(rc, counts_diff[symbol], counts[symbol]); return symbol; } @@ -278,7 +273,7 @@ struct rice_t static struct rice_t riceX IBSS_ATTR_DEMAC; static struct rice_t riceY IBSS_ATTR_DEMAC; -static inline void update_rice(struct rice_t* rice, int x) +DEMAC_INLINE void update_rice(struct rice_t* rice, int x) { rice->ksum += ((x + 1) / 2) - ((rice->ksum + 16) >> 5); @@ -294,7 +289,8 @@ static inline void update_rice(struct rice_t* rice, int x) } } -static inline int entropy_decode3980(struct rice_t* rice) +DEMAC_INLINE int entropy_decode3980(struct rangecoder_t* rc, + struct rice_t* rice) { int base, x, pivot, overflow; @@ -302,11 +298,11 @@ static inline int entropy_decode3980(struct rice_t* rice) if (UNLIKELY(pivot == 0)) pivot=1; - overflow = range_get_symbol_3980(); + overflow = range_get_symbol(rc, counts_3980, counts_diff_3980); if (UNLIKELY(overflow == (MODEL_ELEMENTS-1))) { - overflow = range_decode_short() << 16; - overflow |= range_decode_short(); + overflow = range_decode_short(rc) << 16; + overflow |= range_decode_short(rc); } if (pivot >= 0x10000) { @@ -322,17 +318,17 @@ static inline int entropy_decode3980(struct rice_t* rice) */ lo_bits = (nbits - 16); - base_hi = range_decode_culfreq((pivot >> lo_bits) + 1); - range_decode_update(1, base_hi); + base_hi = range_decode_culfreq(rc, (pivot >> lo_bits) + 1); + range_decode_update(rc, 1, base_hi); - base_lo = range_decode_culshift(lo_bits); - range_decode_update(1, base_lo); + base_lo = range_decode_culshift(rc, lo_bits); + range_decode_update(rc, 1, base_lo); base = (base_hi << lo_bits) + base_lo; } else { /* Codepath for 16-bit streams */ - base = range_decode_culfreq(pivot); - range_decode_update(1, base); + base = range_decode_culfreq(rc, pivot); + range_decode_update(rc, 1, base); } x = base + (overflow * pivot); @@ -346,24 +342,25 @@ static inline int entropy_decode3980(struct rice_t* rice) } -static inline int entropy_decode3970(struct rice_t* rice) +DEMAC_INLINE int entropy_decode3970(struct rangecoder_t* rc, + struct rice_t* rice) { int x, tmpk; - int overflow = range_get_symbol_3970(); + int overflow = range_get_symbol(rc, counts_3970, counts_diff_3970); if (UNLIKELY(overflow == (MODEL_ELEMENTS - 1))) { - tmpk = range_decode_bits(5); + tmpk = range_decode_bits(rc, 5); overflow = 0; } else { tmpk = (rice->k < 1) ? 0 : rice->k - 1; } if (tmpk <= 16) { - x = range_decode_bits(tmpk); + x = range_decode_bits(rc, tmpk); } else { - x = range_decode_short(); - x |= (range_decode_bits(tmpk - 16) << 16); + x = range_decode_short(rc); + x |= (range_decode_bits(rc, tmpk - 16) << 16); } x += (overflow << tmpk); @@ -380,24 +377,24 @@ void init_entropy_decoder(struct ape_ctx_t* ape_ctx, unsigned char* inbuffer, int* firstbyte, int* bytesconsumed) { - bytebuffer = inbuffer; - bytebufferoffset = *firstbyte; + rc.bytebuffer = inbuffer; + rc.bytebufferoffset = *firstbyte; /* Read the CRC */ - ape_ctx->CRC = read_byte(); - ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); - ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); - ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); + ape_ctx->CRC = read_byte(&rc); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(&rc); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(&rc); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(&rc); /* Read the frame flags if they exist */ ape_ctx->frameflags = 0; if ((ape_ctx->fileversion > 3820) && (ape_ctx->CRC & 0x80000000)) { ape_ctx->CRC &= ~0x80000000; - ape_ctx->frameflags = read_byte(); - ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); - ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); - ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); + ape_ctx->frameflags = read_byte(&rc); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(&rc); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(&rc); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(&rc); } /* Keep a count of the blocks decoded in this frame */ ape_ctx->blocksdecoded = 0; @@ -409,13 +406,13 @@ void init_entropy_decoder(struct ape_ctx_t* ape_ctx, riceY.ksum = (1 << riceY.k) * 16; /* The first 8 bits of input are ignored. */ - skip_byte(); + skip_byte(&rc); - range_start_decoding(); + range_start_decoding(&rc); /* Return the new state of the buffer */ - *bytesconsumed = (intptr_t)bytebuffer - (intptr_t)inbuffer; - *firstbyte = bytebufferoffset; + *bytesconsumed = (intptr_t)rc.bytebuffer - (intptr_t)inbuffer; + *firstbyte = rc.bytebufferoffset; } void ICODE_ATTR_DEMAC entropy_decode(struct ape_ctx_t* ape_ctx, @@ -424,8 +421,12 @@ void ICODE_ATTR_DEMAC entropy_decode(struct ape_ctx_t* ape_ctx, int32_t* decoded0, int32_t* decoded1, int blockstodecode) { - bytebuffer = inbuffer; - bytebufferoffset = *firstbyte; + /* local copies, for the compiler to keep in registers */ + struct rangecoder_t r = rc; + struct rice_t rx = riceX, ry = riceY; + + r.bytebuffer = inbuffer; + r.bytebufferoffset = *firstbyte; ape_ctx->blocksdecoded += blockstodecode; @@ -439,25 +440,29 @@ void ICODE_ATTR_DEMAC entropy_decode(struct ape_ctx_t* ape_ctx, } else { if (ape_ctx->fileversion > 3970) { while (LIKELY(blockstodecode--)) { - *(decoded0++) = entropy_decode3980(&riceY); + *(decoded0++) = entropy_decode3980(&r, &ry); if (decoded1 != NULL) - *(decoded1++) = entropy_decode3980(&riceX); + *(decoded1++) = entropy_decode3980(&r, &rx); } } else { while (LIKELY(blockstodecode--)) { - *(decoded0++) = entropy_decode3970(&riceY); + *(decoded0++) = entropy_decode3970(&r, &ry); if (decoded1 != NULL) - *(decoded1++) = entropy_decode3970(&riceX); + *(decoded1++) = entropy_decode3970(&r, &rx); } } } if (ape_ctx->blocksdecoded == ape_ctx->currentframeblocks) { - range_done_decoding(); + range_done_decoding(&r); } + rc = r; + riceX = rx; + riceY = ry; + /* Return the new state of the buffer */ - *bytesconsumed = bytebuffer - inbuffer; - *firstbyte = bytebufferoffset; + *bytesconsumed = r.bytebuffer - inbuffer; + *firstbyte = r.bytebufferoffset; }