# LZ4: the C compression library you call from your own code

> LZ4 trades compression ratio for speed, with a decoder that runs at multiple GB/s per core. This review covers the C reference implementation, its block and frame formats, the build, and where it is the wrong choice.

**lz4/lz4** — Extremely Fast Compression algorithm

- Repository: https://github.com/lz4/lz4
- Website: http://www.lz4.org
- Stars: 12,101 · Forks: 1,600
- Language: C
- License: NOASSERTION
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/lz4-lz4

## The problem LZ4 solves: compression that costs less than the I/O it saves

Most compression libraries are judged on ratio. LZ4 is judged on speed, and the README states the target plainly: compression above 500 MB/s per core, decompression in the multiple GB/s per core, with the decoder typically reaching RAM speed limits on multi-core systems. That number is the whole design brief. A codec that decodes at RAM speed is effectively free at the point of use, which changes where compression can be placed in a system. You can compress an in-memory cache, a message on a queue, a page in a swap device, or a block on the wire, and still spend less time on the codec than on the memory copy or the network round trip.

The audience follows from that. LZ4 is a C library first, and the README describes the command line tool as one consumer of it. The people who get the most out of it are writing storage engines, databases, log shippers, caches, and kernel-adjacent code in C or C++, or binding to it from Java, C#, Python, Perl or Ruby through the ports listed on the project homepage. The README also notes that most distributions ship both liblz4 and the lz4 CLI through their package managers, so a large share of users never build it at all.

It is not a replacement for gzip in the sense of producing smaller files. The README's own benchmark table shows LZ4 default at a 2.101 factor against zlib deflate -6 at 3.099. LZ4 is the answer to a different question: how little CPU can I spend to make this data smaller.

## Block format, frame format, and where the acceleration knob sits

LZ4 has two layers, and the README points at both specifications in the doc directory: lz4_Block_format and lz4_Frame_format. The block format is the raw compressed unit. The frame format is what wraps multiple blocks into a stream, and the README is explicit that arbitrarily long files or data streams are compressed using multiple blocks, organized into a frame, and that interoperable versions of LZ4 must respect the frame format. That sentence is the single most important compatibility rule in the project. If you write a raw block and another implementation expects a frame, nothing will decode it.

Speed is tunable at runtime through an acceleration factor, which the README describes as trading compression ratio for faster speed. This is not a rebuild-time setting; it is a parameter you pass when compressing. The same source also ships LZ4_HC, a high compression derivative that trades CPU time for improved compression ratio. The design constraint that ties the two together is stated directly: all versions feature the same decompression speed. So the choice between LZ4 and LZ4_HC is a choice about your writer's CPU budget only. Your readers pay the same price either way, which is an unusually clean separation and the main reason the format is easy to reason about in a pipeline.

The benchmark table gives the shape of that trade. LZ4 default sits at 780 MB/s compression and 4970 MB/s decompression at factor 2.101. LZ4 HC -9 drops to 41 MB/s compression, roughly a nineteenth of the default, for a factor of 2.721, while decompression stays at 4900 MB/s. Those are single-thread numbers on the reference system the README names, and they are the project's own figures, not an independent measurement.

## Building LZ4 from source and compressing a file

The README gives two commands for the C reference build. The first compiles, the second installs and may need root, depending on where your prefix points. The Makefile follows standard Makefile conventions, which the README notes includes staged installs, redirection and command redefinition, and it is compatible with parallel builds through -j.

```bash
make
make install     # this command may require root permissions
```

After the build, the lz4 CLI is available. A first real use is compressing a file and getting it back. The CLI keeps the original by default, so you get a second file alongside it. The man page in programs/lz4.1.md documents the operation modifiers, including the flags for compressing, decompressing and testing a file.

The decompressed file should be byte-identical to the original; LZ4 is lossless. If you only want to know how well a file would compress without writing anything, the CLI's test mode reports the result and discards the output.

On Windows the README points at vcpkg rather than the Makefile. The sequence it gives is a clone of the vcpkg repository, a bootstrap, an integrate step, and then the install itself.

```bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg.exe install lz4
```

The README notes that the vcpkg port is maintained by Microsoft team members and community contributors, and that if the version is out of date you should open an issue or pull request on the vcpkg repository rather than here. That is a real split in ownership worth knowing before you file a bug about a stale package.

## Dictionary compression and the 64KB ceiling

Small files are the weak point of any LZ4-style codec. There is not enough repeated content inside a 2KB record for the matcher to find anything, so the output is close to the input plus framing overhead. LZ4 addresses this with dictionary compression, supported at both the API level and the CLI level, and the README links the zstd Dictionary Builder as the tool to produce dictionaries. The stated effect is a drastic improvement in compression performance on small files.

The constraint is in the same paragraph and it is easy to miss: any input file can be ingested as a dictionary, but only the final 64KB are used. If you build a dictionary from a corpus and your most valuable samples sit at the front, they contribute nothing. This is a hard boundary, not a tunable one, and it means dictionary construction is a curation problem: order matters, and the tail is the part that survives.

A second, softer risk is that dictionaries are a shared artifact between writer and reader. If the writer uses a dictionary and the reader does not, the stream will not decode. LZ4 gives you the mechanism and the builder lives in a different project, so the versioning and distribution of the dictionary file is your problem, not the library's. Nothing in the README describes a dictionary identifier embedded in the frame, so you should assume you need to ship the dictionary alongside the data and keep the two in sync.

## Where LZ4 is the wrong tool

The ratio ceiling is the first limit. At a 2.101 factor on the Silesia Corpus, LZ4 default is beaten by zlib deflate -6 at 3.099 and by Zstandard 1.4.0 -1 at 2.883. If your data is written once and read rarely, archive storage, release artifacts, cold backups, the CPU you save on write is irrelevant and you are paying for it in bytes forever. LZ4 HC narrows the gap to 2.721 but at 41 MB/s it is no longer a fast codec, and at that point the comparison against zlib deflate -6 at 36 MB/s and a 3.099 factor is genuinely uncomfortable for LZ4. The README's table makes that visible rather than hiding it, which is to the project's credit, but it also means the HC tier is a narrow band.

The second limit is the format split. The block format and the frame format are separate specifications, and only the frame format is described as the interoperable one. Code that reaches for the block API to save a few bytes of header is producing something that portable LZ4 tools will not read. That is a design decision you can make, but it should be deliberate.

The third is the integration surface. This is a C library with a C API, and the README's answer for other languages is a list of ports on the project homepage rather than a set of first-party bindings. Ports lag. If you are on the JVM, the version of the Java port you can pull is a separate release train from the C reference, and the README does not claim otherwise.

## LZ4 vs zstd and the other codecs in the same table

The honest comparison is against Zstandard, because both come from the same lineage of ideas about speed-tunable compression, and the README's table puts them side by side. On the reference system, LZ4 default compresses at 780 MB/s to a 2.101 factor and decompresses at 4970 MB/s. Zstandard 1.4.0 at level -1 compresses at 515 MB/s to a 2.883 factor and decompresses at 1380 MB/s. So LZ4 is roughly 1.5x faster on the write path, 3.6x faster on the read path, and produces about 37% more bytes.

That is the trade in one sentence, and it points at the decision rule. If decompression happens on a hot path, LZ4's decoder advantage is the thing you are buying. If storage or bandwidth dominates and CPU is cheap, Zstandard's ratio wins. The README also notes that LZ4 is compatible with dictionary compression and links to Zstandard's Dictionary Builder, so the two projects are not adversaries at the tooling level.

Against gzip the comparison is starker and less interesting. zlib deflate 1.2.11 -6 compresses at 36 MB/s and decompresses at 445 MB/s for a 3.099 factor. LZ4 is more than twenty times faster to compress and eleven times faster to decompress for a third more bytes. Against Snappy 1.1.4 the picture is closer: Snappy compresses at 565 MB/s to 2.091 and decompresses at 1950 MB/s. LZ4 is faster on both axes and slightly better on ratio in this table, though the two formats are close enough that ecosystem and existing bindings usually decide the choice. LZO 2.09 and QuickLZ 1.5.0 land in the same band, both slower than LZ4 on both axes here. All of these figures come from the project's own README, measured with lzbench on the named reference system, and none of them were reproduced for this article.

## Licence, maintenance cost and what upgrading actually involves

The README states that the LZ4 library is provided as open-source software using the BSD 2-Clause license, and the Makefile header carries the same two-clause text with its disclaimer. Note that the repository's declared licence identifier is NOASSERTION, which means the hosting platform has not classified the file automatically; the human-readable statement in the README and Makefile is BSD 2-Clause. If your legal review depends on a machine-readable SPDX identifier, that mismatch is worth resolving before you vendor the source. This is a description of what the files say, not legal advice.

The upgrade cadence is slow and that is mostly a feature. The releases listed are v1.9.3 in 2020, v1.9.4 in 2022, and v1.10.0 in 2024, subtitled the multicores edition. The last push to the dev branch was on 2026-07-01, so work continues between releases. For a format library, infrequent releases mean the wire format is stable, which is what you want when data written by an old version must be readable by a new one. The README's insistence that interoperable implementations respect the frame format is the same promise stated as a rule.

The cost of an upgrade is therefore mostly in the build, not the data. LZ4's Makefile supports staged installs through DESTDIR and prefix redirection, so packaging a new version into a distribution or a container image is the ordinary work of rebuilding a shared library. The one thing to check is the same thing to check at adoption: that every component in your system links the same liblz4, and that any language binding you use has caught up to the C release you are building against.

## Conclusion

Adopt LZ4 when decode latency or write throughput matters more than file size: log pipelines, caches, memory compression, and any path where data is decompressed far more often than it is written. Do not adopt it when the artifact is stored or shipped once and read rarely, since the ratio gap against zlib and Zstandard is real and permanent. Before committing, verify three things: that your readers implement the LZ4 frame format rather than the raw block format, that your build picks up the same liblz4 version on every platform, and that the dictionary path only ever relies on the final 64KB of the dictionary. The repository's last push was on 2026-07-01, so the dev branch is still moving.

## FAQ

### Is LZ4 compression lossless?

Yes. The README describes LZ4 as a lossless compression algorithm, and decompression returns the original bytes. There is no lossy mode and no quality setting that discards data.

### Which compression method is faster, LZ4 or zstd?

LZ4 is faster on both paths in the README's benchmark table. LZ4 default reaches 780 MB/s compression and 4970 MB/s decompression, while Zstandard 1.4.0 -1 reaches 515 MB/s and 1380 MB/s, at the cost of a lower compression factor for LZ4.

### Is LZ4 faster than gzip?

Substantially, according to the README's table. zlib deflate 1.2.11 -6 compresses at 36 MB/s and decompresses at 445 MB/s, against 780 MB/s and 4970 MB/s for LZ4 default, though zlib reaches a higher compression factor of 3.099 versus 2.101.

### Which is better for zRAM compression, LZ4 or zstd?

The README does not discuss zRAM or kernel memory compression, so it gives no basis for choosing between the two there. What it does state is that LZ4 decompresses at multiple GB/s per core while Zstandard 1.4.0 -1 decodes at 1380 MB/s on the reference system, which is the relevant axis if your pages are read far more often than written.

## Sources

- [Issues](https://github.com/lz4/lz4/issues)
- [lz4/lz4 on GitHub](https://github.com/lz4/lz4)
- [Project website](http://www.lz4.org)
- [README](https://github.com/lz4/lz4/blob/dev/README.md)
- [Releases](https://github.com/lz4/lz4/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lz4-lz4
