I’m excited to announce version 1.0.0 of LibDeflate.jl.
What is LibDeflate
LibDeflate.jl wraps Eric Biggers’s libdeflate, which is a fast implementation of gzip, zlib, Adler32, crc32 and DEFLATE.
LibDeflate is significantly faster than CodecZlib. For example, gzip compressing 10 copies of the Bible takes 540 ms for LibDeflate.jl versus 1,748 ms for CodecZlib.jl. The disadvantage is that it does not work with streamed data; you must de/compress entire buffers in memory.
This makes it ideal for when you de/compress many small payloads.
LibDeflate.jl is supposed to be a relatively low-level package which provides a small-ish wrapper around libdeflate. There are fewer high-level convenience functions, but on the flip side the semantics of the functions’ semantics are well-specified.
As a package, LibDeflate.jl as the following features:
Completely inferrible and trimmable
Most operations do not throw, but return error values for easy and efficient error handling
All de/compression methods also work on pointers, and so memory allocated from e.g. a C library can also be de/compressed
Allocations are minimized (though not completely eliminated)
It currently has a only single dependency: libdeflate_jll
Changes in version 1
Version 1 is the only major changes to LibDeflate since its creation in 2021. It all began when an AI agent found eleven (!) bugs in v0.4, despite the package having 100% code coverage.
Looking at the bugs convinced me they were symptoms of API issues, which caused me to revisit some of the API, and it kind of snowballed from there.
Since I was releasing a breaking change anyway, I revisited every single API, so the list below is not a thorough list of changes. However, some notable changes are:
It is now possible to decompress multi-member gzip files
More gzip header metadata is now returned: Flags, extra flags and OS
I also started a native Julia port of the go flate library a couple years ago: GitHub - quinnj/Flate.jl · GitHub, but got caught up with some manual assembly necessary. I bet AI could help push that over the finish line these days.
I’ve wanted faster/better zlib for a while, thanks for publishing this!
I’m not sure I understand. Is it necessarily an issue that you can’t allocate 10 million Compressors at once? Or are you saying the issue is that Julia’s GC doesn’t invoke the finalizers properly to free these objects? And how does your custom allocation function fix this?
FWIW, I can’t reproduce your issue on Julia 1.13 (my Julia seems to GC the Compressors during the loop execution)
Also, why do we necessarily care that it’s a Julia port for ChunkCodecs - what’s the issue with ccall’ing?
I made an attempt on that many years ago for some of the decompression functionality. The port was fairly literal but I skipped some prefetch things. The speed was maybe 2-3 times slower than libdeflate, but still faster than Zlib.
At least on my machine (Linux, Julia 1.13.0-rc3) julia is not calling the finalizers before the system runs out of memory. The custom allocation function in CodecZlib uses jl_malloc instead of malloc so the julia runtime knows how much memory libzlib is using and can trigger GC more frequently.
Yes the ChunkCodecs interface works with ccall (it is deliberately a lowest common denominator for a number of different C libraries), but I think it would be cool to have pure Julia version, especially for JLD2 which is itself a pure Julia implementation of the HDF5 C library.
JLD2 also wants a slight variant of libdeflate’s decompress! function where the output can be resized if the initial output size isn’t large enough.