Wow! As a lurker on this email list, I just want to say: "Wow! This is an impressive piece of work! Super professional! Love the comprehensive testing and write-up! Thank you for your contributions!" Jim------------------------
On Thu, 22 October 2026 at 07:11 AM, olivier tristan <[email protected]> wrote: > > Hi all, > > I work at UVI, where we make virtual instruments and sample libraries. > Our libraries ship their samples as FLAC, and the sampler streams them > from disk while you play. > > Every note that is not already in the cache starts a stream that decodes > FLAC frames on a worker thread. With a lot of voices, decoding is a > noticeable part of the CPU load, so I spent some time profiling libFLAC > on our material. > > The result is a series of 7 patches against current master: > > https://github.com/UVI-Dev/flac-fork/commits/decode-speedups > (https://github.com/UVI-Dev/flac-fork/commits/decode-speedups) > > Decoding is 36 to 44% faster on Apple M1 and 21 to 43% faster on x86-64, > depending on compiler and material, with bit-identical output. I > measured every commit on its own on both platforms and only kept changes > that are not slower anywhere. Each commit message has its numbers. > > In short: > > 1. Rice parameter 0 partitions go through the fast block reader instead > of one read_unary call per residual. Helps quiet passages (decay tails, > which our samples have plenty of), no change on dense music. > 2. The stop bit and the LSBs are dropped from the bit buffer with one > shift instead of two. The buffer goes from one residual to the next > through that chain, so one operation less there shows. > 3. undo_channel_coding() tests side_subframe_in_use once per frame > instead of once per sample, so the side-coded loops vectorize with MSVC > and clang-cl (clang on macOS already managed). > 4. The "decoded sample fits bps" check no longer exits early, so it > vectorizes too. > 5. LPC and fixed subframes are restored while the residual is read, > instead of decoding the residual first and running lpc_restore_signal() > in a second pass. > 6. Frame CRC-16 with PMULL folding on arm64. > 7. Frame CRC-16 with PCLMULQDQ on x86-64, chosen at run time. > > Patch 5 is where most of the gain is. The rice decoding is bound by the > bit position, the restore by the previous samples, and in one loop the > two chains overlap. We got the idea from dr_flac, which does this. > Residuals are still stored for the client. > > For orders up to 12 the loop is instantiated per order, and the > prediction of the next sample but its newest tap is computed while the > current sample is restored. That way the sample to sample dependency is > one multiply-add, the shift and the add, whatever the compiler does with > the additions. > > The newest sample is kept in a variable, but the 11 older ones only > where there are enough registers. On x86 a window of 12 does not fit, > both MSVC and clang spill it and shift it in memory for every sample, so > there the older samples are read back from the output buffer. On arm64 > reading them back costs up to 5%. > > So bitreader.c picks the window with FLAC__CPU_X86_64 / FLAC__CPU_IA32: > x86 reads the older samples back from the buffer, other architectures > keep all 12 in variables. It comes down to the number of registers, and > each choice measured best on its own side. > > About 6 and 7: the table CRC-16 was 10 to 20% of the decoding time. We > first considered an option to skip it for frames that follow a valid > one, but computing it faster keeps every frame checked and now costs > almost nothing, so there is no such option in this series. > > Numbers (ns per sample, all channels, full decode from memory, best of 8 > runs, CRC checked): > > master series change > Apple M1, clang 15 > 16-bit samples 5.65 3.18 -43.7% > 24-bit samples 6.86 3.91 -43.1% > 16-bit music 7.01 4.47 -36.2% > > Core Ultra 9 185H, MSVC 19.29 > 16-bit samples 6.69 4.58 -31.6% > 24-bit samples 9.39 5.49 -41.6% > 16-bit music 7.83 5.75 -26.5% > > Core Ultra 9 185H, clang-cl 23 > 16-bit samples 5.76 4.02 -30.2% > 24-bit samples 7.08 5.12 -27.7% > 16-bit music 6.52 5.18 -20.6% > > Core Ultra 9 185H, MSVC 19.51 (VS 2026) > 16-bit samples 7.66 4.45 -41.8% > 24-bit samples 7.91 5.58 -29.4% > 16-bit music 9.57 5.42 -43.4% > > With MSVC 19.51, master itself is slower than with 19.29 on 16-bit > samples and music (and faster on 24-bit), while the series runs at the > same speed with both. Better compare the absolute numbers there. > > Testing: the whole test suite passes (ctest, including the streams test) > on macOS arm64, and the decoded output is identical to master for every > commit on all the sets above. flac -t passes on all the test files on > Windows. The PCLMULQDQ CRC was also checked against a bitwise CRC-16 on > random data. > > Every commit builds on its own without new warnings with clang -Wall > -Wextra, and the final state has no new warning with MSVC 19.29 (default > and /W4) and 19.51. > > Things I would like your opinion on: > > - bitreader.c now knows about LPC prediction (it includes lpc.h), and > the per-order instantiation leans on macros around the deduplicated rice > reader. I tried to keep it close to how the BMI2 variant is done, but > there may be a layout you prefer. > - I have only tested arm64 macOS and x86-64 Windows. Linux/GCC results > would be welcome. > > I can split this into separate pull requests if that is easier to > review, starting with 1 to 4 which are small. > > Thanks, > > -- > Olivier Tristan > Research & Development > www.uvi.net (http://www.uvi.net) > > _______________________________________________ > flac-dev mailing list -- [email protected] (mailto:[email protected]) > To unsubscribe send an email to [email protected] > (mailto:[email protected])
_______________________________________________ flac-dev mailing list -- [email protected] To unsubscribe send an email to [email protected]
