An MPEG audio codec in Zig that decodes Layers I, II and III and encodes Layers II and III.

Rearrange the filterbank's rows with shuffles

Twenty-six hundred times real time, from twenty-five, and the polyphase filterbank is an eighth cheaper: 858 ns per granule to 750. Output unchanged again -- ffmpeg agrees to within one least significant bit and forty thousand fuzz rounds are clean. The cost there was not the arithmetic. Each of the 64 rows the standard's matrix produces is a rearrangement of the 32 values actually computed -- row i is D[16 + i], D[32] is zero, and above that D[u] is -D[64 - u] -- and that was being done with sixty-odd scalar moves per slot. As two shuffles of the computed vector and two sign patterns it is six vector operations. The inverse MDCT's accumulator is now split three ways, which is worth about a percent. Three guesses about the filterbank were wrong, and measuring each took less time than arguing would have. Splitting its accumulator four ways to break the multiply-add dependency chain was 6% slower: a 32 lane multiply-add is already four independent 8 lane ones, so the parallelism was there and four accumulators only spilled registers -- two measured identically to one, which confirms it. And matrixing two slots per pass over the four kilobyte coefficient table, on the theory that loads rather than arithmetic were the bound, changed nothing. The README says so; a reader deserves to know what has already been tried and what the machine actually said. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TzKTzcZxD6h1TY6UVD6TMo


Author jcollie.dev
Co-author Claude Opus 5 (1M context) Date Commit Parent c93bcea5
+57 -38
3 changed files
+57 -38
3 changed files