[RFC] Replace LEB128 with Huffman - #7029
Conversation
|
clang-format 20 needs to be run on this PR. (execution 31238927343 / attempt 1) |
|
I think better would be to first run a pass of @anematode idea of compressing unused rows, and then run compression on the resulting net. |
|
also keep in mind that we distribute the binary under gzip... what is the savings there? |
|
My gziped binary goes from 71,556,261 to 69,743,794 (Default compression level). Maybe we would no longer have to distribute under gzip. AFAIK gzip was only added after NNUE was introduced due to the large binaries but was considered a compromise because people now have to unzip after downloading. |
Why not both? There are still other artifacts in the archive that benefit from compression. Also, the decompression argument is moot because a tarball needs to untarred also, and decompression happens at the same time. Same number of clicks for user in UI, or same command in terminal. |
|
And with transport compression that could be used transparently in a browser (like on Lichess): Before: After: So doesn't stack significantly worse, if we even still want to stack transport compression on top. |
Startup time is a lot slower... so we'd need to optimize the decompression fist |
f1aaae7 to
12e05a1
Compare
|
The main reason it takes longer is that we now also compress the 8 bit weights(which there are many more of) rather than just the 16 bit weights. (LEB128 doesn't work on 8 bit weights.) That of course is how we get the extra 25MB size reduction. I pushed a new version that hopefully should get the startup time penalty to under 0.5s. I don't know if 0.5s is a big deal or not. |
|
We could probably get a lot better performance, close to the original, by encoding/decoding multiple Huffman streams at once Latest version: |
|
At this point I'd rather consider using an already established compressor like zstd to make it easier for other tools to process networks. https://github.com/welcome-to-the-sunny-side/misa77 could also be considered if we value decompression speed so much |
|
I have no strong preference. I did this because while LEB128 was a good choice when we had all 16-bit weights it's not now for mostly 8-bit weights. I wanted to avoid any external dependencies just like the current LEB128 does. Especially because we need C++ here and presumably Python on the trainer side. Finally, I don't really know to what degree we care about decompression speed or if an extra 0.416s matters. (I can update this w/ the @anematode suggestion to decode multiple streams in parallel if deemed worth while.) Requesting maintainer guidance. |
|
My concern about decompression speed is more about perf on lower-end devices, including on wasm... idk. Maybe unimportant |
996144c to
97b48d5
Compare
No functional change bench: 2829394
|
I pushed an improved version. My stripped universal Windows binary built w/ GCC 16.1 goes from: The new non gzipped binary is only 674,374 Bytes bigger than the current gzipped binary. |
I asked Opus 5 to replace the LEB128 compression with Huffman and this is what it produced. The net goes from 95,144,073 bytes to 70,836,201 saving about 25MB in the binary. If people think this is a good change I will "attempt" a matching PR on the trainer side although I may need lots of help there.
[Edit]
I pushed an improved version.
The current net goes from
95,144,073 Bytes to 68,640,242 Bytes so a 26.5MB or 28% size reduction.
My stripped universal Windows binary built w/ GCC 16.1 goes from:
99,842,204 to 73,460,380
and gzipped w/ default compression they become:
72,786,006 and 70,032,562 respectively.
Note the new non gzipped binary is only 674,374 Bytes bigger than the current gzipped binary.
No functional change
bench: 2829394