Most Linux distributions ship zlib without Arm-specific optimizations. This means instruction extensions such as Neon Single Instruction Multiple Data (SIMD) and ARMv8 CRC32 are not used, leaving significant performance on the table for compression-heavy workloads.
Designed for modern systems, zlib-ng is an actively maintained fork of zlib that includes the following enhancements:
zlib-ng also supports a zlib-compatible API mode, allowing you to use it as a drop-in replacement without recompiling your applications.
All Neoverse servers and processors implementing Armv8-A and above have support for Neon (Advanced SIMD) and CRC32 instructions.
To check which capabilities a Linux system exposes, use lscpu and look at the Flags output:
lscpu | grep -E "asimd|crc32"
The asimd flag indicates Neon (Advanced SIMD) support. The crc32 flag confirms hardware-accelerated CRC32. Both are present on all modern Arm server platforms, including AWS Graviton, Azure Cobalt 100, and Google Axion.
Install the packages you need for this Learning Path:
sudo apt update
sudo apt install -y build-essential git cmake
Ubuntu and Debian put zlib in /usr/lib/aarch64-linux-gnu.
You can inspect the default library with objdump to see whether it contains any Neon or CRC32 instructions before you replace it:
objdump -d /usr/lib/aarch64-linux-gnu/libz.so.1 | grep -cE "crc32|pmull|v[0-9]+\.(16b|8b|8h|4h|4s|2s|2d|1d)"
Recent Ubuntu releases on aarch64 typically return a non-zero value here — the system zlib has some Neon code, but the coverage is partial. zlib-ng provides dedicated Neon paths for adler32, inflate chunk copying, slide hash, and compare256, with significantly more instruction-level parallelism than the default library. This makes installing zlib-ng worthwhile on any Arm server regardless of what the system zlib already contains.
Clone the zlib-ng source and build it in zlib-compatible mode. The ZLIB_COMPAT=ON option produces a standard libz.so that is API- and ABI-compatible with the system zlib, which allows you to use LD_PRELOAD to switch libraries without recompiling your applications.
git clone https://github.com/zlib-ng/zlib-ng.git
cd zlib-ng
mkdir build && cd build
cmake .. -DZLIB_COMPAT=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_LIBDIR=lib
cmake --build .
sudo cmake --install .
sudo ldconfig
Without -DCMAKE_INSTALL_LIBDIR=lib, CMake’s GnuInstallDirs on Ubuntu aarch64 installs libraries into /usr/local/lib/aarch64-linux-gnu/ instead of /usr/local/lib/. Setting this explicitly keeps paths consistent across platforms.
If successful, zlib-ng installs to /usr/local/lib:
ls /usr/local/lib/libz*
The output is similar to:
/usr/local/lib/libz.a
/usr/local/lib/libz.so
/usr/local/lib/libz.so.1
/usr/local/lib/libz.so.1.3.1.zlib-ng
Query the dynamic linker cache to confirm zlib-ng is visible:
/sbin/ldconfig -p | grep "libz.so"
The output will show both the system zlib and the newly installed zlib-ng:
libz.so.1 (libc6,AArch64) => /usr/local/lib/libz.so.1
libz.so.1 (libc6,AArch64) => /lib/aarch64-linux-gnu/libz.so.1
libz.so (libc6,AArch64) => /usr/local/lib/libz.so
Because zlib-ng is a shared library, you can configure which version an application uses without relinking it.
Navigate back to your home directory before creating the test files:
cd $HOME
Use a text editor to save the following code in a file named test.c:
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"
int main()
{
gzFile myfile;
printf("%s\n", zlibVersion());
myfile = gzopen("testfile.gz", "wb");
gzprintf(myfile,"Hello gzipped file!\n");
gzclose(myfile);
exit(0);
}
Compile the example program, linking against the system zlib:
gcc test.c -o test -lz
Run the program to confirm the system zlib version:
./test
The output will be the version of the system library. For example:
1.3
Use ldd to confirm which shared library the binary loads:
ldd ./test
The output will show the shared libraries used:
linux-vdso.so.1 (0x0000ffffababe000)
libz.so.1 => /lib/aarch64-linux-gnu/libz.so.1 (0x0000ffffaba52000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffab8df000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffaba8e000)
To run the same binary with zlib-ng instead of the default library, set LD_PRELOAD to point to the installed zlib-ng shared object:
LD_PRELOAD=/usr/local/lib/libz.so.1 ./test
The LD_PRELOAD environment variable tells the dynamic linker to load this library before the system default.
The output shows the zlib-ng version:
1.3.1.zlib-ng
The version identifier includes the .zlib-ng suffix to distinguish it from the upstream library.
In this section, you built, installed, and tested zlib-ng.
In the next section you will use zlib-ng to accelerate a Python application that does data compression.