Skip to content

Split View: 압축 알고리즘 Deep Dive — LZ77, Huffman, Arithmetic, ANS, Zstandard, Brotli 완전 정복 (2025)

|

압축 알고리즘 Deep Dive — LZ77, Huffman, Arithmetic, ANS, Zstandard, Brotli 완전 정복 (2025)

TL;DR

  • 무손실 압축은 거의 항상 두 단계의 조합: (1) Dictionary 기반 중복 제거 (LZ77 계열 — 반복 바이트를 back-reference로 치환), (2) Entropy coding (Huffman/Arithmetic/ANS — 흔한 심볼에 짧은 코드).
  • LZ77 (1977): 슬라이딩 윈도우에서 반복을 찾아 (offset, length, next_char) 출력. 모든 modern compressor의 뿌리.
  • Huffman coding: 빈도 기반 prefix-free 코드. DEFLATE/gzip의 entropy 단계.
  • Arithmetic coding: Huffman보다 좋은 비율이지만 느림. 30년간 특허 + 성능 문제로 한정적 사용.
  • ANS (Asymmetric Numeral Systems), Jarek Duda 2009: Arithmetic 품질 + Huffman 속도. Zstd, LZFSE, AV1이 사용. 최근 10년의 hidden 혁명.
  • DEFLATE (1993, RFC 1951): LZ77 + Huffman. gzip, PNG, ZIP의 기본.
  • LZ4 (2011): 압축 비율 희생, 극한 속도 (GB/s). Snappy와 경쟁, 현대 시스템(RocksDB, LZ4-HC)에 내장.
  • Zstandard (2016, Yann Collet): LZ77 + FSE(ANS). gzip보다 3배 빠르고 비율 좋음. Dictionary training으로 작은 메시지도 효율적 압축.
  • Brotli (2015, Google): Zstd와 유사 경쟁자. 웹 트래픽 최적화, 정적 dictionary 내장.
  • 실무 선택: 속도 → LZ4, 범용 → Zstd, 웹 정적 콘텐츠 → Brotli, 레거시 → gzip, 작은 메시지 많음 → Zstd dictionary.

1. 압축의 기본

1.1 왜 압축하는가

디스크: 스토리지 비용 절감. 네트워크: 대역폭 절감, 레이턴시 감소. 메모리: RAM 효율 증가. 백업/아카이브: 장기 보관.

2025년 데이터가 폭증하면서 압축의 중요성은 오히려 증가. CPU는 남지만 I/O와 네트워크가 병목.

1.2 Lossless vs Lossy

Lossless (이 글의 주제):

  • 원본 완전 복원.
  • 텍스트, 코드, DB, 실행 파일.
  • gzip, zstd, LZ4, Brotli.

Lossy:

  • 일부 정보 손실 (품질 vs 비율 trade-off).
  • 이미지(JPEG), 오디오(MP3), 비디오(H.264).
  • 이 글의 범위 밖.

1.3 Shannon의 한계

정보 이론 (Claude Shannon, 1948):

어떤 데이터든 압축의 이론적 하한이 존재한다. 엔트로피:

H(X)=ipilog2piH(X) = -\sum_i p_i \log_2 p_i

p_i는 심볼 i의 확률. 단위는 bit/symbol.

예: 영어 텍스트의 엔트로피는 약 1.5 bit/char. 8-bit ASCII로 저장된 텍스트는 이론상 5배 이상 압축 가능.

실제 압축기는 이 한계에 가까워지려 한다. 하지만 정확히 도달 불가능.

1.4 두 단계 전략

현대 무손실 compressor의 대부분이 두 단계를 결합:

  1. Dictionary / LZ: "ABCDEF...ABCDEF"처럼 반복된 시퀀스를 back-reference로 치환. 구조적 중복 제거.
  2. Entropy coding: 남은 바이트들의 통계적 편향 활용. "a"가 자주 나오면 짧은 코드, "z"는 긴 코드.

두 단계는 독립적으로 발전. 어느 LZ, 어느 entropy를 조합하느냐에 따라 다른 compressor.


2. LZ77 — 모든 것의 뿌리

2.1 아이디어

Lempel와 Ziv의 1977년 논문. "반복된 시퀀스를 이전 위치 참조로 치환".

입력: ABABABAB

과정:
A"A" (리터럴)
B"B" (리터럴)
AB"이전 위치 -2, 길이 2 복사"
AB"이전 위치 -2, 길이 2 복사"

출력:

(0, 0, A)
(0, 0, B)
(2, 2, _)
(2, 2, _)

각 토큰: (offset, length, next_char). Offset = 얼마나 뒤로, length = 몇 바이트 복사.

2.2 슬라이딩 윈도우

실제로는 최근 N 바이트의 윈도우만 검색.

[search buffer (N bytes)] [lookahead buffer (M bytes)] [미처리 데이터]

매 step:

  1. Lookahead의 첫 바이트들이 search buffer의 어디에 나타났는지 찾기.
  2. 가장 긴 match를 찾음.
  3. (offset, length, next_char) 출력.
  4. Window를 length + 1만큼 전진.

2.3 LZSS (1982)

LZ77의 개선: match가 너무 짧으면 back-reference가 오히려 손해. (3 byte reference가 3 byte 리터럴과 같음.)

LZSS: flag bit으로 "다음이 리터럴인지 reference인지" 표시. 3 byte 이상만 reference.

대부분 modern compressor는 LZSS variant.

2.4 LZ78와 LZW

LZ78: slide window 대신 명시적 dictionary 구축. Dictionary가 시퀀스 index를 저장.

LZW (Welch, 1984): LZ78의 변형. GIF, Unix compress, 구 PDF.

지금은 거의 쓰이지 않음. LZ77 family가 지배.

2.5 Back-reference의 효율

예: 1 MB 반복 텍스트

  • Raw: 1 MB.
  • LZ77 (window 32 KB): 처음 ~32 KB 이후는 모두 "뒤로 32 KB, 길이 ..." reference. 수 바이트로 표현.
  • 최종: ~32 KB + metadata.

32배 압축. 원리는 단순: "반복을 가리킨다".

2.6 Hash-based Matching

실제 구현에서는 매 위치에서 "가장 긴 match" 찾기가 병목. 해결: hash chain.

각 3-byte prefix에 해시를 계산하고, 같은 해시의 이전 위치들을 링크드 리스트로 연결. Match 후보를 빠르게 찾음.

속도 vs 압축률 trade-off:

  • Hash chain 길게: 더 많은 후보 검사 → 더 긴 match → 더 나은 압축 → 느림.
  • 짧게: 빠름, 압축률 낮음.

이것이 gzip의 "-1 (fast)" vs "-9 (best)" 차이.


3. Huffman Coding

3.1 아이디어

David Huffman의 1952년 알고리즘 (MIT 대학원생 과제). "자주 나오는 심볼에 짧은 코드, 드문 심볼에 긴 코드".

예: 텍스트에서 문자 빈도

a: 40%
b: 20%
c: 15%
d: 15%
e: 10%

고정 길이 3-bit 코드면 symbol 당 3 bit. 평균 3 bit/symbol.

Huffman 코드:

a → 0     (1 bit)
b → 10    (2 bits)
c → 110   (3 bits)
d → 1110  (4 bits)
e → 1111  (4 bits)

평균: 0.4×1 + 0.2×2 + 0.15×3 + 0.15×4 + 0.10×4 = 2.25 bit/symbol. 25% 절약.

3.2 Prefix-free Property

Huffman 코드는 prefix-free — 어떤 코드도 다른 코드의 접두사가 아니다. 즉 0, 10, 110 같이. 이 덕분에 구분자 없이 연속 decode 가능.

01001101110

0 10 0 110 1110a b a c d

3.3 Huffman Tree

이진 트리를 만들어 각 심볼에 경로 할당:

        *
       / \
      *   a
     / \
    *   b
   / \
  *   c
 / \
d   e
  • Leaf가 심볼.
  • 경로가 코드 (왼쪽=0, 오른쪽=1).
  • 자주 나오는 심볼이 짧은 경로.

3.4 Tree 구축

  1. 모든 심볼을 우선순위 큐에 빈도순으로.
  2. 가장 작은 두 노드를 꺼내 부모 노드로 합침 (빈도 = 합).
  3. 부모를 큐에 삽입.
  4. 큐에 하나만 남을 때까지 반복.

O(n log n) 시간. 탐욕 알고리즘이지만 최적 prefix-free 코드임이 증명됨.

3.5 Canonical Huffman

표준 Huffman tree는 여러 가능한 형태. Canonical form은 길이만 저장하고 규칙적으로 할당 → 압축 메타데이터 작음.

DEFLATE와 대부분 현대 compressor는 canonical Huffman 사용.

3.6 한계

Huffman의 한계:

  • 최소 1 bit/symbol. 심볼 확률이 50%를 초과해도 더 줄일 수 없음.
  • 확률이 50%인 경우 (예: fair coin) — 이상적이지만 비효율 아님.
  • 확률이 90%인 경우 (예: biased data) — 이론상 0.15 bit/symbol가 필요한데 Huffman은 1 bit → 85% 손실.

이 한계를 Arithmetic codingANS가 극복한다.


4. Arithmetic Coding

4.1 아이디어

"전체 메시지를 하나의 실수로 표현". 어떤 확률 분포에도 이론적 한계에 근접.

4.2 예제

알파벳 a (50%), b (25%), c (25%). 인코딩할 메시지: "abc".

초기 범위: [0, 1).

  1. 'a' 입력: 범위 = [0, 0.5).
  2. 'b' 입력: 0.5 × 0.5 = 0.25 길이, offset 0.25. 범위 = [0.25, 0.375).
  3. 'c' 입력: 0.125 × 0.25 = 0.031, offset 0.09375. 범위 = [0.34375, 0.375).

이 범위 안의 아무 실수나 출력. 예: 0.3501011010 (이진 표현).

Decoder는 같은 확률 분포로 역산. 0.35 × 2 = 0.7 → 범위 [0, 0.5) 미포함 → ... (실제로는 실수를 추적).

4.3 효율

각 심볼이 정확히 -log2(p) bit를 차지. Shannon 엔트로피 한계에 근접.

Huffman: 정수 bit만 (1, 2, 3...). Arithmetic: 실수 bit (0.5, 1.5, 2.3...).

4.4 왜 한정적 사용

단점:

  1. 속도: 부동소수점 연산 또는 정밀 정수 산술. Huffman보다 2-5배 느림.
  2. 특허 문제: IBM의 여러 특허로 수십 년간 사용 제한. 2000년대 이후 만료.
  3. 복잡도: 구현 어려움, bug 취약.

30년 동안 "더 나은 기법이 있지만 실용적이지 않다"는 위치. H.264, JPEG 2000에서만 활용.

4.5 Range Coding

Arithmetic coding의 변형. 정수 산술 사용, 특허 문제 없음. 일부 compressor (7zip의 LZMA)가 사용.


5. ANS — 게임 체인저

5.1 배경

2009년 폴란드 수학자 Jarek Duda가 새 엔트로피 코딩을 제안. ANS (Asymmetric Numeral Systems). 이메일 리스트와 논문에 조용히 공개.

처음엔 관심 적었다. 하지만 곧 **"Arithmetic의 품질 + Huffman의 속도"**라는 평가를 받으며 급속히 채택.

5.2 아이디어

"큰 정수 하나"에 전체 메시지를 인코딩하되, 표준 레지스터 연산으로.

Arithmetic coding처럼 작동하지만 부동소수점 없음, 나눗셈 거의 없음. 대신 Finite State Machine으로 표현.

5.3 Finite State Entropy (FSE)

ANS의 실용 변형. Yann Collet (Zstd 저자)이 구현.

  • 각 심볼에 다음 상태 할당.
  • 인코딩: 심볼 입력 → 상태 전이 → 일부 bit 출력.
  • 디코딩: 반대 방향.

상태 머신이 pre-computed table → 런타임에 lookup + shift → 매우 빠름.

5.4 tANS vs rANS

tANS (table-based ANS): 상태 전이 table. 빠르지만 큰 table → 메모리 오버헤드.

rANS (range-ANS): 범위 기반. table 없음, 약간 느림, 더 유연.

Zstd의 FSE는 tANS. Facebook의 일부 내부 도구는 rANS.

5.5 성능

FSE vs Huffman vs Arithmetic:

항목HuffmanArithmeticFSE
압축률기준-0.5% ~ -3%-0.5% ~ -3%
인코딩 속도1x (기준)0.3-0.5x1.2-2x (더 빠름!)
디코딩 속도1x0.3-0.5x1.5-2x

"아무 단점 없는 업그레이드". 왜 진작 없었는지 의문.

5.6 채택

ANS 기반 compressor:

  • Zstandard (2016): FSE 사용. 가장 널리 쓰임.
  • LZFSE (Apple, 2015): LZ + FSE. iOS/macOS 기본.
  • AV1 비디오 codec: 엔트로피 단계에 ANS.
  • oodle: 상용 게임 압축.
  • Brotli: 일부 부분.

2015년 이후 나온 compressor의 사실상 표준 entropy coder.


6. DEFLATE — gzip의 엔진

6.1 개요

Phil Katz의 1993년 작품. PKZIP에 사용, 이후 RFC 1951 (1996)로 표준화.

LZ77 + Huffman의 결합. 가장 널리 쓰이는 압축 포맷:

  • gzip (.gz).
  • ZIP.
  • PNG (이미지).
  • HTTP Content-Encoding: gzip.

30년 넘게 표준.

6.2 구조

압축된 데이터 = block 시퀀스. 각 block:

  • Header: block 타입 (stored, fixed Huffman, dynamic Huffman).
  • Huffman tree (dynamic만).
  • Compressed data: LZ77 토큰들, Huffman으로 인코딩.

6.3 LZ77 단계

  • Window: 32 KB.
  • Match length: 3-258 바이트.
  • Offset: 1-32768.

각 match는 (length, distance) 쌍. 리터럴은 0-255 문자 코드.

6.4 Huffman 단계

두 Huffman 트리:

  • Literal/length tree: 리터럴 문자(0-255) + length 코드(257-285).
  • Distance tree: offset 코드(0-29).

길이와 거리는 log-scale로 "bucket"에 매핑. 작은 값은 정확히, 큰 값은 extra bits로.

6.5 레벨

gzip -1 ~ gzip -9:

  • -1: lazy match, 빠른 hash chain.
  • -9: 더 긴 hash chain, best match 탐색.

Time/space trade-off. 실제 차이는 ~20% 압축률 / 5-10x 속도.

6.6 성능

  • 압축: ~50 MB/s (단일 코어).
  • 해제: ~300 MB/s.
  • 비율: 텍스트 기준 3-5x.

30년 전 알고리즘이지만 여전히 쓸 만. 단, 현대 대안들(zstd, brotli)이 더 나음.


7. LZ4 — 극한 속도

7.1 철학

Yann Collet의 2011년 작품. "압축률을 희생해서 속도를 극대화".

7.2 디자인

  • No entropy coding: LZ77만, Huffman/ANS 없음.
  • Simple format: byte-aligned, no bit manipulation.
  • Hash table: 단순한 해시 함수, 한 번의 lookup만.
  • Small match minimum: 4 바이트.

7.3 속도

  • 압축: 500-800 MB/s (단일 코어).
  • 해제: 2-5 GB/s (단일 코어).
  • 비율: 텍스트 기준 2-2.5x (gzip의 절반).

"압축률은 낮지만 매우 빠르다". 네트워크/디스크 bottleneck보다 CPU가 빠른 환경에 적합.

7.4 사용처

  • RocksDB, Cassandra: 디스크 저장 압축.
  • Zstandard 내부: 낮은 level에서 LZ4-like 모드.
  • MongoDB: Snapshot, journal.
  • Kernel: Linux의 일부 압축 경로.
  • Kafka: 일부 설정.

"실시간 압축이 필요하지만 CPU 예산이 제한된" 모든 곳.

7.5 LZ4 HC

"High Compression" 변형. 더 느리지만 더 나은 압축률. 여전히 zstd보다는 빠르고 덜 압축.

7.6 Snappy

Google의 유사 compressor (2011). LZ4와 비슷한 특성:

  • Very fast, 낮은 비율.
  • LevelDB, BigTable, HBase에 사용.

LZ4가 약간 더 빠르고 약간 더 압축. 대부분 LZ4 승.


8. Zstandard — 범용의 왕

8.1 배경

Facebook의 Yann Collet (LZ4 저자 동일). 2016년 발표.

목표: "gzip보다 3배 빠르면서 더 잘 압축".

8.2 구성

  • LZ77-style match: 2+ 바이트, 큰 window(기본 8 MB).
  • Entropy coding: FSE (ANS 기반) for literals, Huffman for literal distribution in some cases.
  • Frame format: 청크 단위, 스트리밍 친화적.

8.3 Dictionary 기능

Killer feature: pre-trained dictionary.

# 100개 샘플 JSON으로 dictionary 학습
zstd --train samples/*.json -o dict.bin

# Dictionary 사용해 압축
zstd -D dict.bin input.json -o output.zst

# Dictionary 사용해 해제
zstd -D dict.bin -d output.zst -o input.json

효과: 작은 메시지(수백 바이트)에 dictionary가 10-100x 압축률 향상.

일반 compressor는 작은 메시지에 약한데 zstd dictionary로 극복. Kafka, 작은 HTTP API, 채팅 메시지에 이상적.

8.4 Long-range Mode

zstd --long: 멀리 떨어진 match까지 검색 (GB 단위 window). 백업, 디스크 이미지에 효과적.

8.5 Compression Levels

zstd -1 (fast) ~ zstd -22 (max):

  • -1: ~500 MB/s, ratio 2x. LZ4와 경쟁.
  • -3 (기본): ~200 MB/s, ratio 3x.
  • -9: ~50 MB/s, ratio 3.5x.
  • -19: ~10 MB/s, ratio 4.2x.
  • -22: ~1 MB/s, ratio 4.5x.

넓은 범위. 상황에 따라 선택 가능.

8.6 Decompression 속도

2-3 GB/s (단일 코어). Compression level과 무관. 항상 빠름 — 이게 가장 큰 장점.

8.7 채택

  • Linux 커널 압축 (zstd 옵션).
  • Btrfs, ZFS 파일시스템.
  • Arch Linux 패키지 (전체 repo zstd로).
  • Kafka, RocksDB 등 현대 시스템.
  • Facebook 전체 인프라.

사실상 새 표준.

8.8 코드 복잡도

Zstd source: ~25,000 줄 C. gzip은 ~5,000 줄. Trade-off: 복잡도 vs 성능.

단일 바이너리, 외부 의존성 없음 → 포팅 쉬움.


9. Brotli — Google의 선택

9.1 배경

Google Jyrki Alakuijala 등, 2015년 발표. HTTP/2 컨텐츠 압축용.

특징:

  • 정적 dictionary 내장: 120 KB의 영어/HTML/CSS/JS 토큰.
  • Context modeling: literal 주변 context를 고려해 더 나은 Huffman 트리.
  • 긴 back-reference: 2 MB window.

9.2 압축률

Brotli는 zstd와 유사한 압축률약간 느림.

텍스트, 특히 HTML/CSS/JS에 최적화. 웹 콘텐츠에는 zstd보다 조금 더 좋음 (내장 dictionary 덕분).

9.3 Decompression

~400 MB/s. Zstd보다 약간 느림. 하지만 충분.

9.4 사용처

주로 웹:

  • HTTP Content-Encoding: br. Chrome, Firefox 모두 지원.
  • WOFF2 (Web Open Font Format 2).
  • Android APK.
  • Cloudflare 엣지 압축.

일반 시스템에서는 zstd가 주로 사용.

9.5 Brotli vs Zstd (웹)

10 MB HTML + CSS + JS
gzip (-9):   2.5 MB
brotli (11): 1.8 MB  (28% better than gzip)
zstd (19):   1.9 MB

Brotli가 웹에서 약간 더 좋음. 정적 dictionary 효과.


10. LZMA / 7z

10.1 배경

Igor Pavlov의 LZMA (Lempel-Ziv-Markov chain-Algorithm). 7-zip 압축기의 기본. 1998년부터.

10.2 특징

  • Huge window: 최대 4 GB. 매우 멀리 있는 match도 찾음.
  • Arithmetic coding (range coder): Huffman보다 더 나은 비율.
  • Markov model: literal 주변 context로 조건부 확률 개선.
  • 매우 복잡: 구현 이해 어려움.

10.3 성능

  • 압축: 매우 느림 (수 MB/s).
  • 해제: 수십 MB/s.
  • 비율: 매우 좋음. Zstd보다 5-15% 더 압축.

10.4 사용처

  • 7-zip: GUI 압축 도구.
  • xz (Unix): .tar.xz.
  • Arch Linux (이전엔 xz, 지금은 zstd로 전환 중).
  • Debian packages (.deb): 일부.

"극한 압축률이 필요하고 시간이 있을 때". 백업, 배포 파일에.

10.5 왜 zstd가 대체

Zstd: 약간 나쁜 비율, 훨씬 빠름. 대부분 경우 합리적 선택.

Arch가 xz → zstd로 전환하면서 "설치 시간이 50% 감소, 패키지 크기는 20% 증가"라고 보고. 사용자 입장에서 net positive.


11. 비교 표

11.1 압축률

고정된 입력(텍스트, 10 MB Linux kernel source):

알고리즘RatioCompressDecompress
LZ4 -12.1x700 MB/s3500 MB/s
LZ4 -9 (HC)2.5x30 MB/s3500 MB/s
Snappy2.0x600 MB/s2500 MB/s
gzip -12.7x200 MB/s400 MB/s
gzip -93.2x20 MB/s400 MB/s
zstd -12.8x500 MB/s2500 MB/s
zstd -33.4x250 MB/s2500 MB/s
zstd -93.6x80 MB/s2500 MB/s
zstd -194.0x10 MB/s2500 MB/s
brotli -114.1x8 MB/s400 MB/s
xz -94.3x3 MB/s70 MB/s

11.2 용도별 권장

실시간 스트리밍 / 고속 I/O:

  • LZ4: 압축률은 낮지만 병목 없음.
  • 예: 로그 스트림, 인메모리 캐시.

범용 파일 압축:

  • Zstd -3: 합리적 압축, 빠름.
  • gzip의 현대적 대체.

백업 / 아카이브:

  • Zstd -19 또는 xz: 한 번 압축, 여러 번 해제.
  • 압축 시간 허용 가능.

웹 콘텐츠:

  • Brotli: 정적 dictionary로 HTML/CSS/JS 최적화.
  • 폴백 gzip.

작은 메시지 많음:

  • Zstd with dictionary: 학습된 dictionary로 10-100배 개선.
  • 예: Kafka 메시지, 작은 HTTP API, 채팅.

DB / 스토리지 엔진:

  • LZ4: 빠른 read.
  • Zstd: 공간 효율 우선.

12. SIMD 가속

12.1 압축의 SIMD 기회

압축은 이론적으로 sequential 작업이지만 많은 부분에서 SIMD 적용 가능:

  1. Hash 계산: 여러 포지션의 hash를 한 번에.
  2. Match 검색: 긴 byte comparison.
  3. Huffman decoding: 여러 심볼 병렬 lookup.
  4. Entropy decode: ANS state update.

12.2 현대 구현

Zstd: SSE2/AVX2 지원. SIMD로 literal copying 가속.

LZ4: 매우 단순한 구조라 SIMD 활용 적음. 하지만 decompression 시 긴 복사는 memcpy (SIMD 내장).

libdeflate: zlib의 대안 구현. SIMD 가속으로 zlib보다 2-3배 빠른 gzip.

12.3 대역폭의 한계

많은 경우 메모리 대역폭이 실제 병목. SIMD로 CPU 연산을 줄여도 L3/DRAM 대역폭 포화.

압축 속도가 수 GB/s에 도달하면 하드웨어 한계.

12.4 하드웨어 압축

Intel QAT (Quick Assist): CPU 내부에 DEFLATE 하드웨어 가속기. 100 GB/s 수준.

NVIDIA GPU: cuDNN의 일부, 일부 DB 엔진.

SmartNIC: 네트워크 카드가 압축/해제.

대규모 서비스(Facebook, Google)에서 활용.


13. Dictionary Training

13.1 왜 필요한가

일반 compressor는 입력 안의 중복만 찾음. 100 byte JSON 메시지에는 내부 중복이 거의 없음 → 압축 안 됨.

{"name":"Alice","age":30}

이 메시지를 압축하면 오히려 커질 수 있음 (메타데이터 오버헤드).

13.2 Dictionary의 역할

수백 개의 유사한 JSON을 보면 공통 패턴이 많다:

  • "name": 자주 나옴.
  • "age": 자주 나옴.
  • 키 이름 전반적으로 반복.

Dictionary = "이 공통 토큰들을 미리 담아둔 참조 자료".

압축 시 reference를 dictionary로 가리키면 매우 짧은 코드.

13.3 학습 과정

zstd --train samples/*.json --maxdict=110K -o dict
  1. 샘플 파일들 읽기.
  2. 가장 흔한 substring을 찾기 (통계).
  3. 상위 substring들을 dictionary에 포함.
  4. Dictionary 파일 출력.

13.4 사용

zstd -D dict input.json -o output.zst

Dictionary 파일을 encoder와 decoder 모두가 가지고 있어야 함. 공유 필요.

13.5 성능

실측:

  • 일반 JSON 100 byte: zstd로 90 byte (10% 감소).
  • Dictionary 사용 (10 KB dictionary, 1000개 JSON 학습): 30 byte (70% 감소).

7배 차이. 작은 메시지 대량 전송에 필수.

13.6 사용 사례

  • Kafka: 메시지별로 작은 payload. Dictionary로 50-80% 감소.
  • HTTP API: 응답 payload 구조가 정해짐. Dictionary 학습 가능.
  • Protobuf messages: 특정 스키마에 특화된 dictionary.
  • NoSQL KV: 비슷한 키-값 쌍.

Facebook은 이 기법으로 petabyte 규모 저장 비용 절감.


14. 실전 예제

14.1 HTTP 서버

Accept-Encoding: gzip, br, zstd

Response:
Content-Encoding: zstd

Nginx:

http {
    zstd on;
    zstd_comp_level 3;
    zstd_types text/html text/css application/javascript application/json;
}

(zstd module 설치 필요. 기본 Nginx는 gzip.)

정적 파일은 build 시점에 미리 압축 (.br, .gz 파일) → runtime CPU 절약.

14.2 Kafka

compression.type=zstd

Producer가 배치를 압축. 기본 3 레벨 적용 시 텍스트 메시지가 3-5x 감소.

Kafka는 압축된 배치를 그대로 저장 + 전달. Broker는 압축 해제 안 함 → CPU 효율.

14.3 Kubernetes 컨테이너 이미지

OCI image layers는 보통 gzip. zstd로 전환 중:

docker build --compression=zstd .

Pull 시간 30-50% 감소. 큰 이미지(ML model)에 유용.

14.4 Backup

tar -cf - /data | zstd -19 --long > backup.tar.zst

--long으로 큰 window 사용 → 파일 간 중복도 제거.

Rsync + zstd는 전통적 gzip 조합보다 10-30% 빠르고 작음.

14.5 DB

RocksDB:

options.compression = kZstdCompression;
options.bottommost_compression = kZstdCompression;
options.compression_opts.level = 3;

L0-L4는 LZ4 (빠른), L5-L6는 zstd (높은 압축). Tiered compression.

ClickHouse:

CREATE TABLE events (
    ...
) ENGINE = MergeTree
SETTINGS
    index_granularity = 8192;

ALTER TABLE events MODIFY COLUMN data CODEC(ZSTD(3));

컬럼별 다른 코덱 가능.


15. 현대 동향

15.1 Asymmetric Speed

Decode >> Encode 속도 불균형을 활용:

  • 한 번 압축, 수천 번 해제 (웹, CDN, 이미지).
  • Encoder에 시간 투자해 decoder 빠름.
  • Brotli -11, zstd -19.

15.2 Hardware Acceleration

  • Intel QAT (서버 CPU).
  • NVIDIA decompression engines (Ampere+).
  • AWS Nitro card 일부.

대규모 서비스에서 주요 투자 영역.

15.3 ML-based

Google DeepMind의 실험: LLM 기반 압축. 모델이 "다음 토큰을 예측"하면 차이만 저장.

결과: 이미지/텍스트에서 5-10배 압축. 하지만 느리고 GPU 필요 — 실용적이지 않음.

하지만 특수 용도(genome, 과학 데이터)에서 관심.

15.4 Lossy Text?

"일부 오차 허용" 텍스트 압축 실험 중. 자연어의 정보 이론 한계는 여전히 1-1.5 bit/char 수준이지만 LLM이 이를 더 낮출 가능성.

학술 연구 단계.

15.5 새 entropy coder?

ANS 이후 10년. 차세대는?

Pelican, rANS 변형 등이 실험 중. 하지만 ANS가 충분히 좋아서 주류 교체는 당분간 없을 듯.


16. 자주 묻는 질문

Q: 이미 압축된 파일을 또 압축하면?

A: 효과 거의 없음. JPEG, MP3, ZIP, 이미 gzip 된 파일 등. 대부분 1% 미만 절감, 심지어 커질 수 있음 (메타데이터).

Q: Solid 압축 vs 파일별 압축?

A: Solid (전체를 하나의 stream): 파일 간 중복 활용, 더 좋은 압축률. 단점: 하나만 꺼내려면 전체 해제. 파일별: 독립적, 빠른 random access. 단점: 공유 중복 손실.

ZIP는 파일별, tar.gz / tar.zst / 7z는 solid.

Q: 최대 효율 조합?

A: LZMA / zstd --long 둘 다 수 GB window로 큰 파일 간 중복 활용.

Q: 암호화와 함께?

A: 압축 → 암호화 순서. 반대는 암호화된 데이터가 랜덤해서 압축 안 됨. 주의: CRIME/BREACH 같은 length-based 공격. HTTPS에서 sensitive data + compression 혼합 시 위험.

Q: Streaming 지원?

A: Zstd, brotli, gzip 모두 streaming API. 입력을 한 번에 가지고 있지 않아도 압축 가능.


17. 요약 — 한 장 정리

┌─────────────────────────────────────────────────────┐
Compression Cheat Sheet├─────────────────────────────────────────────────────┤
│ 기본 원리:│   두 단계: LZ (중복 제거) + Entropy (통계)│                                                       │
LZ 패밀리:LZ77 (slide window, back-reference)LZSS (length minimum)LZ78/LZW (explicit dictionary, deprecated)│                                                       │
Entropy Coding:Huffman (1952): 정수 bit, 빠름                     │
Arithmetic (1970s): 실수 bit, 느림, 특허            │
ANS/FSE (2009): Arithmetic 품질 + Huffman 속도     │
│                                                       │
│ 주요 알고리즘:DEFLATE (1993): LZ77 + HuffmanLZ4 (2011): LZ77, 초고속                         │
Zstandard (2016): LZ77 + FSEBrotli (2015): 정적 dictionary + context          │
LZMA (1998): LZ77 + Markov + Range│                                                       │
│ 속도 vs 비율:LZ4/Snappy: 빠름, 낮은 비율                         │
│   gzip -1: 빠름, 중간                                │
│   zstd -3: 균형 (기본 권장)│   gzip -9: 느림, 좋은 비율                            │
│   zstd -19: 느림, 더 좋은 비율                        │
│   brotli -11: 느림, 웹 최적                           │
│   xz -9: 매우 느림, 최고 비율                         │
│                                                       │
│ 용도별:│   실시간: LZ4│   범용: zstd -3│   백업: zstd -19 또는 xz                              │
│   웹: brotli                                          │
│   작은 메시지: zstd + dictionary                      │
│                                                       │
Dictionary Training:Zstd --train                                        │
│   작은 메시지 10-100x 개선                            │
Kafka, HTTP API, NoSQL에 유용                      │
│                                                       │
│ 가속:SIMD (libdeflate, zstd)하드웨어 (Intel QAT)GPU│                                                       │
│ 주의:│   이미 압축된 것 재압축 불가                            │
│   암호화 전에 압축                                     │
CRIME/BREACH 주의 (HTTPS)└─────────────────────────────────────────────────────┘

18. 퀴즈

Q1. 무손실 압축이 대부분 "두 단계"인 이유는?

A. 두 종류의 중복이 각각 다른 방법으로 해결되기 때문. (1) 구조적 중복: "ABCDEFABCDEF"처럼 시퀀스가 반복되는 경우 — LZ77 family의 back-reference가 완벽. 수 바이트로 긴 반복을 참조. 하지만 이 단계는 단일 심볼의 통계적 편향은 다루지 못함. (2) 통계적 편향: "e" (21%), "a" (8%), "z" (0.1%) 같은 불균형한 심볼 분포 — entropy coding(Huffman/ANS)이 짧은 코드를 흔한 심볼에 할당. 두 단계를 분리하면 각각 최적화 가능. 현대 compressor(DEFLATE/Zstd/Brotli) 모두 이 패턴. 이론적으로 "한 번에 모두 처리"하는 방법도 있지만(PPM, context mixing) 구현 복잡도와 속도 때문에 실용적이지 못함. "분해해서 정복"의 교과서 사례.

Q2. Huffman coding의 근본적 한계는?

A. 최소 1 bit/symbol 이라는 정수 제약. Huffman은 각 심볼에 prefix-free 정수 bit 길이의 코드를 할당. 심볼 확률이 50%라면 이상적 비트는 1 (완벽). 확률이 25%면 이상적 2 bit (완벽). 하지만 확률 90%라면 이상적 ~0.15 bit가 필요한데 Huffman은 최소 1 bit — 85% 손실. 텍스트에서 공백, 'e' 같은 고빈도 심볼이 이런 경우. Shannon 엔트로피 한계에 접근하지 못함. Arithmetic coding과 ANS는 "한 심볼에 0.15 bit"를 수학적으로 표현 — "여러 심볼을 모아 정수 bit에 pack". Huffman 대비 5-15% 더 좋은 압축률. 1952년부터 60년 동안 Huffman이 주류였던 이유는 속도와 특허 자유 — 그러다 ANS(2009)가 등장해 양쪽 문제를 모두 해결했다. 현대 compressor(zstd, LZFSE)가 Huffman 대신 ANS로 이동한 배경.

Q3. ANS가 "game changer"로 불리는 이유는?

A. Arithmetic coding의 압축률을 Huffman 속도로 달성. 30년 동안 "Huffman은 빠르지만 suboptimal, Arithmetic은 optimal이지만 느리고 특허"라는 교착 상태였다. ANS(2009, Jarek Duda)는 새로운 수학적 접근으로 둘을 동시에 해결: (1) 압축률: Arithmetic과 거의 동등, Huffman보다 5-10% 좋음, (2) 속도: Huffman보다 1.5-2배 빠름 (shift + lookup만, 부동소수점/나눗셈 없음), (3) 특허 자유: Duda가 의도적으로 공개. 상용 채택 순서: LZFSE (Apple 2015) → Zstd (Facebook 2016) → AV1 (2018). 현대 compressor에서 "Huffman 대신 FSE/ANS"가 표준이 됨. "빠른 것과 좋은 것 중 하나"를 양자택일해야 했던 classic CS trade-off가 깨진 드문 사례. 왜 더 일찍 발견되지 않았는지가 CS 역사의 미스터리.

Q4. Zstandard의 dictionary training이 해결하는 문제는?

A. 작은 메시지의 내부 중복 부재. 일반 compressor(gzip, LZ4)는 입력 파일 내부의 반복만 찾는다. 100 byte JSON {"name":"Alice","age":30} 같은 메시지는 내부에 거의 반복이 없음 → 압축률 ~5% 또는 역효과(메타데이터로 오히려 커짐). 하지만 수천 개의 유사한 JSON을 모아 보면 "name":, "age": 같은 키가 매우 반복적. Dictionary training: (1) 샘플 파일들에서 가장 흔한 substring 추출 → ~10 KB dictionary, (2) 이 dictionary를 encoder와 decoder 양쪽에 배포, (3) 압축 시 dictionary를 "가상 prefix"로 사용 → 작은 메시지도 dictionary 참조로 70% 이상 감소. 실측: 100 byte JSON이 일반 zstd로 90 byte → dictionary zstd로 30 byte (7배 차이). Facebook, Kafka, HTTP API가 이 기법으로 메시지 페이로드를 50-80% 절감. 단점: dictionary 공유 필요, 다른 데이터 타입엔 효과 없음. "압축은 상관관계 활용"이라는 원칙을 extra context(dictionary)로 확장한 것.

Q5. LZ4가 gzip보다 압축률이 낮아도 DB에서 선호되는 이유는?

A. 압축 해제 속도가 DB 성능의 병목. DB는 디스크에서 데이터를 읽고 즉시 사용해야 한다. Read path: disk read → decompress → query. gzip decompress(~400 MB/s)가 SSD read(~5 GB/s)의 10배 느리면 CPU가 I/O를 따라가지 못함. LZ4는 3-5 GB/s decompress — SSD 속도에 근접. 압축률이 2x vs gzip 3x 차이로 저장 공간을 더 쓰지만, 읽기 성능이 10배 빠름. 실시간 쿼리에서 이 trade-off가 중요. RocksDB의 tiered compression 전략: 자주 읽히는 L0-L1은 LZ4, 드물게 읽히는 L5-L6는 zstd. "hot data는 빠른 압축, cold data는 높은 비율". Cassandra도 유사. "압축률이 최우선이 아니라 전체 pipeline의 throughput"이 핵심 — 개별 단계 최적화가 시스템 최적화와 다르다는 교훈.

Q6. Brotli가 HTTP에서 Zstd보다 나은 이유는?

A. 정적 dictionary 내장 + HTML/CSS/JS 튜닝. Brotli는 120 KB 크기의 내장 dictionary를 가진다 — 영어 단어, HTML 태그, CSS 속성, JavaScript 키워드가 미리 포함. 웹 페이지 압축 시 이 dictionary를 "가상 prefix"처럼 사용 → 작은 페이지도 매우 압축. Zstd는 내장 dictionary가 없어서 dictionary 없이 사용 시 웹 콘텐츠에서 약간 뒤진다. 실측: 10 MB 웹 콘텐츠에서 brotli -11이 zstd -19보다 5-10% 작다. 추가로 Brotli는 context modeling이 더 정교 — literal 주변 context를 고려한 Huffman 트리 선택. 단점: Brotli compression이 느려서 빌드 타임 precompression 필요(*.html.br 파일). Runtime 압축은 zstd가 여전히 유리. 현대 웹 stack: 정적 파일은 brotli precompressed(.br), 동적 응답은 gzip 또는 zstd. Cloudflare 같은 CDN은 엣지에서 이를 관리. "같은 문제라도 사용 패턴에 따라 다른 알고리즘이 최적"의 사례.

Q7. 이미 압축된 파일(JPEG, MP3, ZIP)을 다시 압축하면 왜 효과 없는가?

A. 압축된 데이터는 통계적으로 랜덤에 가까움. 압축의 기본 원리가 "중복 제거 + 편향 활용"인데, 잘 압축된 데이터는 정의상 중복이 거의 없고 분포가 균일하다. LZ77이 찾을 back-reference가 없고(모든 시퀀스가 유일), Huffman/ANS가 활용할 편향이 없음(모든 심볼이 거의 균등). Shannon의 한계에 이미 가까워서 더 줄일 여지가 없다. 재압축 결과: (1) ~0-1% 절감 (noise level), (2) 메타데이터 오버헤드로 오히려 증가, (3) CPU만 낭비. 예외: (a) 이미 압축된 파일들 사이의 중복 — 같은 파일을 여러 번 압축한 tar → solid 압축으로 일부 절감, (b) lossy → lossless 재인코딩 — JPEG을 PNG로 변환 후 압축(다른 게 됨). 실무 규칙: MIME 타입이 이미 압축된 것(image/jpeg, video/mp4, application/zip)은 HTTP compression skip. Nginx의 gzip_types에 이런 타입 제외는 이 때문. "압축은 관련성(redundancy)을 먹고 산다, 랜덤에서는 배고프다".


19. 학습 리소스

논문:

  • Lempel & Ziv, "A Universal Algorithm for Sequential Data Compression" (1977).
  • Huffman, "A Method for the Construction of Minimum-Redundancy Codes" (1952).
  • Duda, "Asymmetric Numeral Systems" (2009).
  • Yann Collet, Zstd 관련 블로그 글들.

:

  • "Introduction to Data Compression" — Khalid Sayood.
  • "Managing Gigabytes" — Witten, Moffat, Bell.

블로그:

도구:

소스:

  • Facebook/zstd GitHub.
  • LZ4 GitHub.
  • Brotli GitHub.

이 글이 도움이 됐다면 다음 포스트도 확인해 보세요:

  • "SIMD / AVX / NEON Deep Dive" — 압축 가속에 쓰이는 기법.
  • "RocksDB & LSM-Tree" — LZ4/zstd를 사용하는 DB 엔진.
  • "CDN & Edge Caching" — 웹 압축의 배포.
  • "Binary Serialization (Protobuf/Thrift/Avro)" — 사전 압축된 데이터 포맷.

Compression Algorithms Deep Dive — LZ77, Huffman, Arithmetic, ANS, Zstandard, Brotli (2025)

TL;DR

  • Lossless compression is almost always a two-stage combination: (1) dictionary-based duplicate removal (LZ77 family — replace repeated bytes with back-references), and (2) entropy coding (Huffman/Arithmetic/ANS — short codes for frequent symbols).
  • LZ77 (1977): find repeats in a sliding window and emit (offset, length, next_char). The root of every modern compressor.
  • Huffman coding: frequency-based prefix-free codes. The entropy stage of DEFLATE/gzip.
  • Arithmetic coding: better ratio than Huffman but slow. Patents plus performance kept it niche for 30 years.
  • ANS (Asymmetric Numeral Systems), Jarek Duda 2009: arithmetic quality at Huffman speed. Used by Zstd, LZFSE, AV1 — the quiet revolution of the last decade.
  • DEFLATE (1993, RFC 1951): LZ77 + Huffman. The basis of gzip, PNG, ZIP.
  • LZ4 (2011): sacrifices ratio for extreme speed (GB/s). Competes with Snappy; embedded in modern systems (RocksDB, LZ4-HC).
  • Zstandard (2016, Yann Collet): LZ77 + FSE (ANS). Roughly 3x faster than gzip with better ratios. Dictionary training makes even tiny messages compress well.
  • Brotli (2015, Google): a Zstd peer tuned for web traffic with a built-in static dictionary.
  • In practice: speed -> LZ4, general -> Zstd, static web assets -> Brotli, legacy -> gzip, many small messages -> Zstd with dictionary.

1. Fundamentals

1.1 Why compress

  • Disk: lower storage cost.
  • Network: saves bandwidth, reduces latency.
  • Memory: better RAM utilization.
  • Backup/archive: long-term retention.

Data growth in 2025 only increases the importance of compression. CPU is plentiful; I/O and network are the bottlenecks.

1.2 Lossless vs Lossy

Lossless (this post):

  • Perfect reconstruction.
  • Text, code, DB, executables.
  • gzip, zstd, LZ4, Brotli.

Lossy:

  • Accepts information loss (quality vs ratio trade-off).
  • Images (JPEG), audio (MP3), video (H.264).
  • Out of scope here.

1.3 Shannon's limit

Claude Shannon (1948) proved every data source has a theoretical floor — its entropy:

H(X)=ipilog2piH(X) = -\sum_i p_i \log_2 p_i

where p_i is the probability of symbol i. Units are bits per symbol.

English text has entropy around 1.5 bits/char. An 8-bit ASCII file could theoretically be compressed more than 5x. Real compressors approach but never reach that bound.

1.4 The two-stage strategy

Modern lossless compressors combine two stages:

  1. Dictionary / LZ: replace repeated sequences such as "ABCDEF...ABCDEF" with back-references. Removes structural redundancy.
  2. Entropy coding: exploit statistical bias in the remaining bytes. Frequent bytes get short codes; rare bytes get long ones.

The two stages evolve independently; different compressors pair different LZ and entropy components.


2. LZ77 — the root of it all

2.1 Idea

Lempel and Ziv's 1977 paper: replace repeated sequences with a back-reference to an earlier position.

Input: ABABABAB

A          -> "A" (literal)
B          -> "B" (literal)
AB         -> "copy from -2, length 2"
AB         -> "copy from -2, length 2"

Output:

(0, 0, A)
(0, 0, B)
(2, 2, _)
(2, 2, _)

Each token is (offset, length, next_char).

2.2 Sliding window

Real implementations only search the last N bytes.

[search buffer (N bytes)] [lookahead buffer (M bytes)] [unprocessed data]

Each step: find where the lookahead prefix last appeared in the search buffer, pick the longest match, emit (offset, length, next_char), advance by length + 1.

2.3 LZSS (1982)

Improvement over LZ77: emitting a 3-byte reference for a 3-byte match is a wash. LZSS uses a flag bit to distinguish literal from reference and only emits references of 3+ bytes. Most modern compressors are LZSS variants.

2.4 LZ78 and LZW

LZ78: builds an explicit dictionary instead of using a sliding window. LZW (Welch, 1984) is the variant used by GIF, Unix compress, and old PDF. Rarely used today — the LZ77 family dominates.

2.5 Back-reference efficiency

1 MB of repeated text:

  • Raw: 1 MB.
  • LZ77 (32 KB window): the first ~32 KB is literal, everything afterwards becomes "copy back 32 KB, length ...".
  • Result: about 32 KB + metadata.

32x compression from a single mechanism: "point at the repetition".

2.6 Hash-based matching

Finding the longest match at each position is the bottleneck. The classic fix: hash chains. Hash every 3-byte prefix; chain the previous positions with the same hash. Candidate matches are found quickly.

Speed vs ratio trade-off:

  • Longer chains: more candidates -> longer matches -> better ratio -> slower.
  • Shorter chains: faster, lower ratio.

This is exactly gzip -1 vs gzip -9.


3. Huffman coding

3.1 Idea

David Huffman, 1952 (a graduate assignment at MIT): frequent symbols get short codes, rare symbols get long codes.

Example frequencies:

a: 40%
b: 20%
c: 15%
d: 15%
e: 10%

Fixed 3-bit codes: 3 bits/symbol. Huffman:

a -> 0     (1 bit)
b -> 10    (2 bits)
c -> 110   (3 bits)
d -> 1110  (4 bits)
e -> 1111  (4 bits)

Average: 0.4x1 + 0.2x2 + 0.15x3 + 0.15x4 + 0.10x4 = 2.25 bits/symbol. A 25% saving.

3.2 Prefix-free property

No code is a prefix of another, so the stream decodes unambiguously with no separators:

01001101110 -> 0 10 0 110 1110 -> a b a c d

3.3 Huffman tree

A binary tree where leaves are symbols, the path is the code (left=0, right=1), and frequent symbols sit on shorter paths.

3.4 Tree construction

  1. Put every symbol into a priority queue by frequency.
  2. Pop the two smallest nodes, merge into a parent whose frequency is their sum.
  3. Push the parent back.
  4. Repeat until one node remains.

O(n log n). Greedy but provably optimal for prefix-free codes.

3.5 Canonical Huffman

The same frequency table admits many tree shapes. Canonical form stores only the code lengths and assigns codes by rule, shrinking metadata. DEFLATE and most modern compressors use canonical Huffman.

3.6 Limits

Huffman has a floor of 1 bit/symbol. For a symbol with 90% probability, the ideal is about 0.15 bits but Huffman must emit 1 — an 85% overhead. Arithmetic coding and ANS fix this.


4. Arithmetic coding

4.1 Idea

Represent the entire message as a single real number, approaching the Shannon bound for any distribution.

4.2 Example

Alphabet a (50%), b (25%), c (25%). Encode "abc" starting from the interval [0, 1).

  1. 'a': range = [0, 0.5).
  2. 'b': range = [0.25, 0.375).
  3. 'c': range = [0.34375, 0.375).

Emit any real number inside the final interval (for example 0.35, binary 01011010). The decoder recovers the sequence using the same probability model.

4.3 Efficiency

Each symbol costs exactly -log2(p) bits — essentially reaching Shannon's bound. Huffman uses integer bits; arithmetic uses fractional bits.

4.4 Why it stayed niche

  • Speed: floating-point or precise integer arithmetic, 2-5x slower than Huffman.
  • Patents: IBM patents restricted use for decades (expired in the 2000s).
  • Complexity: hard to implement, easy to get wrong.

For 30 years it was "better in theory, impractical." Seen only in H.264 and JPEG 2000.

4.5 Range coding

An arithmetic-coding variant that uses integer arithmetic and avoided the patents. Used by some compressors (LZMA in 7zip).


5. ANS — the game changer

5.1 Background

In 2009, Polish mathematician Jarek Duda proposed ANS (Asymmetric Numeral Systems), quietly published in papers and mailing lists. Adoption accelerated once people realized ANS delivers arithmetic-quality compression at Huffman speed.

5.2 Idea

Encode the whole message into a single large integer, but using ordinary register operations. Behaves like arithmetic coding without floating point and with almost no division — reformulated as a finite state machine.

5.3 Finite State Entropy (FSE)

The practical variant, implemented by Yann Collet (author of Zstd).

  • Each symbol has a next state assignment.
  • Encode: symbol in -> state transition -> emit some bits.
  • Decode: reverse.

The state machine is pre-computed into a table; runtime is lookup + shift — very fast.

5.4 tANS vs rANS

  • tANS (table-based ANS): state-transition table; fast, memory-heavy.
  • rANS (range-ANS): range-based; no table, slightly slower, more flexible.

Zstd's FSE is tANS; some Facebook internal tools use rANS.

5.5 Performance

PropertyHuffmanArithmeticFSE
Ratiobaseline-0.5% to -3%-0.5% to -3%
Encode speed1x0.3-0.5x1.2-2x (faster!)
Decode speed1x0.3-0.5x1.5-2x

An upgrade with essentially no downside — "why didn't this exist earlier?"

5.6 Adoption

ANS-based compressors:

  • Zstandard (2016): FSE. The most widespread.
  • LZFSE (Apple, 2015): LZ + FSE, default on iOS/macOS.
  • AV1 video codec: ANS entropy stage.
  • oodle: commercial game compression.
  • Brotli: partially.

Effectively the standard entropy coder for new compressors built after 2015.


6. DEFLATE — the gzip engine

6.1 Overview

Phil Katz, 1993, used in PKZIP; standardized as RFC 1951 (1996). LZ77 + Huffman. The most widely used compression format ever: gzip, ZIP, PNG, Content-Encoding: gzip. Still a baseline after 30 years.

6.2 Structure

Compressed data = a sequence of blocks. Each block has:

  • Header: block type (stored, fixed Huffman, dynamic Huffman).
  • Huffman tree (dynamic only).
  • Compressed data: LZ77 tokens encoded with Huffman.

6.3 LZ77 stage

  • Window: 32 KB.
  • Match length: 3-258 bytes.
  • Offset: 1-32768.

Each match is a (length, distance) pair; literals are 0-255 character codes.

6.4 Huffman stage

Two trees:

  • Literal/length tree: literals (0-255) plus length codes (257-285).
  • Distance tree: offset codes (0-29).

Lengths and distances map to log-scale buckets — small values exact, large values encoded with extra bits.

6.5 Levels

gzip -1 through gzip -9:

  • -1: lazy match, short hash chains.
  • -9: longer hash chains, best-match search.

Time/space trade-off — roughly 20% ratio difference across a 5-10x range of speed.

6.6 Performance

  • Compress: ~50 MB/s (single core).
  • Decompress: ~300 MB/s.
  • Ratio: 3-5x on text.

Still serviceable, but modern alternatives (zstd, brotli) are better.


7. LZ4 — extreme speed

7.1 Philosophy

Yann Collet, 2011. Trade ratio for speed.

7.2 Design

  • No entropy coding: LZ77 only.
  • Simple format: byte-aligned, no bit tricks.
  • Hash table: trivial hash function, single lookup.
  • Small minimum match: 4 bytes.

7.3 Speed

  • Compress: 500-800 MB/s (single core).
  • Decompress: 2-5 GB/s (single core).
  • Ratio: about 2-2.5x on text (half of gzip).

Perfect for environments where CPU is faster than the disk or network.

7.4 Where it is used

  • RocksDB, Cassandra: on-disk storage compression.
  • Zstandard: LZ4-like mode at low levels.
  • MongoDB: snapshots and journals.
  • Kernel: some Linux compression paths.
  • Kafka: some configurations.

Anywhere you need real-time compression on a constrained CPU budget.

7.5 LZ4 HC

"High Compression" variant: slower, better ratio. Still faster and less-compressing than zstd.

7.6 Snappy

Google's similar compressor (2011): very fast, low ratio; used in LevelDB, BigTable, HBase. LZ4 is slightly faster and slightly denser — LZ4 usually wins.


8. Zstandard — the general-purpose king

8.1 Background

Yann Collet (again) at Facebook, released 2016. Goal: "3x faster than gzip with better ratios."

8.2 Components

  • LZ77-style match: 2+ bytes, large window (default 8 MB).
  • Entropy coding: FSE (ANS-based) for literals, with Huffman used for literal distribution in some cases.
  • Frame format: chunked, streaming-friendly.

8.3 Dictionary feature

Killer feature: pre-trained dictionaries.

# Train a dictionary from 100 JSON samples
zstd --train samples/*.json -o dict.bin

# Compress using the dictionary
zstd -D dict.bin input.json -o output.zst

# Decompress using the dictionary
zstd -D dict.bin -d output.zst -o input.json

Effect: tiny messages (hundreds of bytes) see 10-100x better ratios. Regular compressors do poorly on small payloads; Zstd dictionaries fix that. Ideal for Kafka, small HTTP APIs, chat messages.

8.4 Long-range mode

zstd --long searches matches over GB-scale windows. Great for backups and disk images.

8.5 Compression levels

zstd -1 (fast) through zstd -22 (max):

  • -1: ~500 MB/s, ratio 2x. Competes with LZ4.
  • -3 (default): ~200 MB/s, ratio 3x.
  • -9: ~50 MB/s, ratio 3.5x.
  • -19: ~10 MB/s, ratio 4.2x.
  • -22: ~1 MB/s, ratio 4.5x.

8.6 Decompression speed

2-3 GB/s single core, independent of compression level. The biggest win.

8.7 Adoption

  • Linux kernel compression (zstd option).
  • Btrfs, ZFS filesystems.
  • Arch Linux packages (entire repo switched to zstd).
  • Kafka, RocksDB, and other modern systems.
  • Facebook's full infrastructure.

The de facto new standard.

8.8 Code complexity

Zstd source is ~25,000 lines of C; gzip is ~5,000. Trade-off: complexity for performance. But it ships as a single binary with no external dependencies, so porting is straightforward.


9. Brotli — Google's pick

9.1 Background

Jyrki Alakuijala et al. at Google, 2015. Designed for HTTP/2 content compression.

Features:

  • Built-in static dictionary: 120 KB of English/HTML/CSS/JS tokens.
  • Context modeling: nearby context shapes the Huffman tree.
  • Long back-references: 2 MB window.

9.2 Ratio

Similar to zstd, but slightly slower. Tuned for text — especially HTML/CSS/JS — where the static dictionary makes it a touch better than zstd.

9.3 Decompression

~400 MB/s. Slightly slower than zstd, still fast enough.

9.4 Where it is used

Mainly the web:

  • HTTP Content-Encoding: br — supported by Chrome and Firefox.
  • WOFF2 (Web Open Font Format 2).
  • Android APK.
  • Cloudflare edge compression.

General-purpose systems tend to use zstd.

9.5 Brotli vs Zstd (web)

10 MB HTML + CSS + JS
gzip (-9):   2.5 MB
brotli (11): 1.8 MB  (28% better than gzip)
zstd (19):   1.9 MB

Brotli wins slightly on the web thanks to its static dictionary.


10. LZMA / 7z

10.1 Background

Igor Pavlov's LZMA (Lempel-Ziv-Markov chain-Algorithm), the default in 7-zip since 1998.

10.2 Features

  • Huge window: up to 4 GB — finds very distant matches.
  • Arithmetic coding (range coder).
  • Markov model: conditional probabilities from literal context.
  • Very complex implementation.

10.3 Performance

  • Compress: very slow (a few MB/s).
  • Decompress: tens of MB/s.
  • Ratio: excellent — 5-15% better than Zstd.

10.4 Where it is used

  • 7-zip: the GUI tool.
  • xz (.tar.xz).
  • Arch Linux (moving from xz to zstd).
  • Some Debian packages.

"When you need extreme ratios and have time." Backups, distribution files.

10.5 Why zstd is displacing it

Zstd: slightly worse ratio, far faster. Arch reported that switching from xz to zstd dropped install time by 50% at the cost of 20% larger packages — a user-side net positive.


11. Comparison tables

11.1 Ratio and speed

Text input (10 MB Linux kernel source):

AlgorithmRatioCompressDecompress
LZ4 -12.1x700 MB/s3500 MB/s
LZ4 -9 (HC)2.5x30 MB/s3500 MB/s
Snappy2.0x600 MB/s2500 MB/s
gzip -12.7x200 MB/s400 MB/s
gzip -93.2x20 MB/s400 MB/s
zstd -12.8x500 MB/s2500 MB/s
zstd -33.4x250 MB/s2500 MB/s
zstd -93.6x80 MB/s2500 MB/s
zstd -194.0x10 MB/s2500 MB/s
brotli -114.1x8 MB/s400 MB/s
xz -94.3x3 MB/s70 MB/s

11.2 Recommendations

  • Real-time streaming / high-throughput I/O: LZ4.
  • General-purpose file compression: Zstd -3.
  • Backup/archive: Zstd -19 or xz.
  • Web content: Brotli (fallback gzip).
  • Many small messages: Zstd with dictionary.
  • DB / storage engines: LZ4 for hot data, Zstd when space matters.

12. SIMD acceleration

12.1 Opportunities

Compression is sequential, but parts vectorize well:

  1. Hash computation across many positions.
  2. Match scanning over long byte comparisons.
  3. Huffman decoding parallel lookups.
  4. Entropy decode ANS state updates.

12.2 Modern implementations

  • Zstd: SSE2/AVX2 support; SIMD accelerates literal copying.
  • LZ4: structure is too simple for heavy SIMD, but long copies use memcpy (SIMD internally).
  • libdeflate: SIMD-accelerated zlib replacement, 2-3x faster gzip.

12.3 Bandwidth ceiling

Beyond a point, memory bandwidth is the real bottleneck. At multi-GB/s compression, L3/DRAM limits dominate.

12.4 Hardware offload

  • Intel QAT: on-chip DEFLATE accelerators, 100 GB/s class.
  • NVIDIA GPU: parts of cuDNN, some DB engines.
  • SmartNIC: compression on the NIC.

Large services (Facebook, Google) invest heavily here.


13. Dictionary training

13.1 Why

Normal compressors exploit redundancy inside the input. A 100-byte JSON has almost no internal redundancy:

{"name":"Alice","age":30}

It may even grow after compression due to metadata.

13.2 What a dictionary does

Across thousands of similar JSONs, strings like "name": and "age": repeat constantly. A dictionary stores those shared tokens so back-references can point into it. References become very short.

13.3 Training

zstd --train samples/*.json --maxdict=110K -o dict
  1. Read samples.
  2. Find the most frequent substrings statistically.
  3. Pack the winners into the dictionary.
  4. Write the dictionary file.

13.4 Usage

zstd -D dict input.json -o output.zst

Both encoder and decoder must share the dictionary.

13.5 Numbers

  • 100-byte JSON with plain zstd: 90 bytes (10% saved).
  • 100-byte JSON with a 10 KB dictionary trained on 1000 samples: 30 bytes (70% saved).

A 7x difference. Essential when shipping many small messages.

13.6 Use cases

  • Kafka: per-message payloads; 50-80% reduction.
  • HTTP APIs: responses with fixed schema.
  • Protobuf messages: schema-specific dictionaries.
  • NoSQL KV: similar key-value pairs.

Facebook uses this at petabyte scale to cut storage.


14. Real examples

14.1 HTTP server

Accept-Encoding: gzip, br, zstd

Response:
Content-Encoding: zstd

Nginx:

http {
    zstd on;
    zstd_comp_level 3;
    zstd_types text/html text/css application/javascript application/json;
}

For static files, precompress at build time (.br, .gz files) to save runtime CPU.

14.2 Kafka

compression.type=zstd

Producers compress batches. Level 3 reduces text messages by 3-5x. Brokers store and forward the compressed batches without decompressing — CPU efficient.

14.3 Kubernetes container images

OCI image layers default to gzip; the ecosystem is moving to zstd:

docker build --compression=zstd .

Pull times drop 30-50%. Big wins for large images (ML models).

14.4 Backup

tar -cf - /data | zstd -19 --long > backup.tar.zst

--long finds cross-file redundancy. Rsync + zstd is 10-30% faster and smaller than gzip pipelines.

14.5 Databases

RocksDB:

options.compression = kZstdCompression;
options.bottommost_compression = kZstdCompression;
options.compression_opts.level = 3;

Tiered compression: LZ4 for L0-L4 (fast reads), zstd for L5-L6 (dense cold data).

ClickHouse:

CREATE TABLE events (
    ...
) ENGINE = MergeTree
SETTINGS
    index_granularity = 8192;

ALTER TABLE events MODIFY COLUMN data CODEC(ZSTD(3));

Different codecs per column.


15.1 Asymmetric speed

Exploit decode-faster-than-encode asymmetry: compress once, decompress many times (web, CDN, images). Invest encoder CPU so the decoder stays fast. Brotli -11, zstd -19.

15.2 Hardware acceleration

  • Intel QAT on server CPUs.
  • NVIDIA decompression engines (Ampere+).
  • AWS Nitro cards.

Major investment area for hyperscalers.

15.3 ML-based compression

Google DeepMind experiments with LLM-based compression: the model predicts the next token, only the delta is stored. 5-10x compression on images and text — but slow and GPU-bound. Interesting for specialty data (genome, scientific).

15.4 Lossy text?

"Some error allowed" text compression is being researched. Natural-language entropy is about 1-1.5 bits/char, and LLMs may push the floor lower. Still academic.

15.5 A successor to ANS?

Ten years in, proposals like Pelican and rANS variants exist, but ANS is good enough that mainstream replacement is unlikely soon.


16. FAQ

Q: Recompressing an already-compressed file?

A: Practically no gain. JPEG, MP3, ZIP, already-gzipped files — usually under 1% savings, sometimes larger due to metadata.

Q: Solid vs per-file compression?

A: Solid (the whole thing is one stream) exploits cross-file redundancy for a better ratio, but you must decompress the whole archive to extract one file. Per-file is independent with random access but loses shared redundancy. ZIP is per-file; tar.gz / tar.zst / 7z are solid.

Q: Best ratio combination?

A: LZMA or zstd --long with multi-GB windows for large files.

Q: Compression with encryption?

A: Compress, then encrypt. Encrypted data is random and uncompressible. Beware CRIME/BREACH-style length attacks: mixing compression with sensitive data over HTTPS is dangerous.

Q: Streaming support?

A: Zstd, Brotli, gzip all have streaming APIs — you can compress without holding the full input in memory.


17. One-page summary

+------------------------------------------------------+
|          Compression Cheat Sheet                     |
+------------------------------------------------------+
| Basics:                                              |
|   Two stages: LZ (dedup) + Entropy (statistics)      |
|                                                      |
| LZ family:                                           |
|   LZ77 (sliding window, back-reference)              |
|   LZSS (minimum match length)                        |
|   LZ78/LZW (explicit dictionary, deprecated)         |
|                                                      |
| Entropy coders:                                      |
|   Huffman (1952): integer bits, fast                 |
|   Arithmetic (1970s): fractional bits, slow, patents |
|   ANS/FSE (2009): arithmetic quality at Huffman speed|
|                                                      |
| Main algorithms:                                     |
|   DEFLATE (1993): LZ77 + Huffman                     |
|   LZ4 (2011): LZ77 only, ultra-fast                  |
|   Zstandard (2016): LZ77 + FSE                       |
|   Brotli (2015): static dictionary + context         |
|   LZMA (1998): LZ77 + Markov + range coder           |
|                                                      |
| Speed vs ratio:                                      |
|   LZ4/Snappy: fast, low ratio                        |
|   gzip -1: fast, medium                              |
|   zstd -3: balanced (default pick)                   |
|   gzip -9: slow, good ratio                          |
|   zstd -19: slow, better ratio                       |
|   brotli -11: slow, web-optimal                      |
|   xz -9: very slow, best ratio                       |
|                                                      |
| Use cases:                                           |
|   Real-time: LZ4                                     |
|   General: zstd -3                                   |
|   Backup: zstd -19 or xz                             |
|   Web: brotli                                        |
|   Small messages: zstd + dictionary                  |
|                                                      |
| Dictionary training:                                 |
|   zstd --train                                       |
|   10-100x improvement on small messages              |
|   Great for Kafka, HTTP APIs, NoSQL                  |
|                                                      |
| Acceleration:                                        |
|   SIMD (libdeflate, zstd)                            |
|   Hardware (Intel QAT)                               |
|   GPU                                                |
|                                                      |
| Caveats:                                             |
|   Do not recompress compressed data                  |
|   Compress before encryption                         |
|   Watch for CRIME/BREACH over HTTPS                  |
+------------------------------------------------------+

18. Quiz

Q1. Why is most lossless compression "two stages"?

A. Because two kinds of redundancy need different tools. (1) Structural redundancy: repeated sequences like "ABCDEFABCDEF" — LZ77's back-references handle this perfectly but cannot address per-symbol statistical bias. (2) Statistical bias: imbalanced symbol distributions such as "e" (21%) vs "z" (0.1%) — entropy coding (Huffman/ANS) assigns short codes to frequent symbols. Separating the two stages means each can be optimized independently. DEFLATE, Zstd, and Brotli all follow this pattern. "Do it all at once" schemes like PPM or context mixing exist but are too complex and slow to be practical — textbook divide-and-conquer.

Q2. What is Huffman coding's fundamental limitation?

A. The 1-bit/symbol integer floor. Huffman gives each symbol a prefix-free code of integer length. Symbols with 50% probability get the ideal 1 bit. Symbols with 25% get 2 bits (ideal). But a symbol with 90% probability ideally needs ~0.15 bits while Huffman emits 1 bit — 85% overhead. High-frequency symbols like spaces or 'e' in text live in this regime, and Huffman cannot reach the Shannon bound. Arithmetic coding and ANS encode "0.15 bits per symbol" by packing multiple symbols into the same integer width, gaining 5-15% better ratios. Huffman dominated from 1952 to the 2000s because of its speed and patent freedom — then ANS (2009) solved both problems, which is why Zstd and LZFSE moved to it.

Q3. Why is ANS called a "game changer"?

A. It delivers arithmetic-coding ratios at Huffman speed. For 30 years the trade-off was "Huffman fast but suboptimal, Arithmetic optimal but slow and patent-encumbered." ANS (2009, Jarek Duda) solved both: (1) ratio near arithmetic, 5-10% better than Huffman; (2) speed 1.5-2x faster than Huffman (shift + lookup, no floats or division); (3) patent-free — Duda deliberately released it openly. Commercial adoption: LZFSE (Apple 2015), Zstd (Facebook 2016), AV1 (2018). "Huffman is replaced by FSE/ANS" is the new standard in modern compressors — a rare case where a classic CS trade-off was actually dissolved. Why it was not found earlier is a CS-history mystery.

Q4. What problem does Zstandard's dictionary training solve?

A. The lack of internal redundancy in small messages. Normal compressors (gzip, LZ4) only find repeats within the input. A 100-byte JSON like {"name":"Alice","age":30} has almost none, so the output is about 5% smaller — or sometimes larger due to metadata. But across thousands of similar JSONs, keys like "name": and "age": repeat heavily. Dictionary training (1) extracts the most common substrings from samples into a ~10 KB dictionary, (2) ships that dictionary to encoder and decoder, (3) uses it as a "virtual prefix" during compression. Result: the 100-byte JSON goes from 90 bytes (plain zstd) to 30 bytes (with dictionary) — a 7x difference. Facebook, Kafka, and HTTP APIs use this to shrink payloads 50-80%. Caveats: the dictionary must be shared; it helps only similar data. The principle — compression feeds on correlation — extended with extra context.

Q5. Why do databases prefer LZ4 even though gzip has a better ratio?

A. Decompression speed is the DB bottleneck. Read path: disk read -> decompress -> query. If gzip decode (~400 MB/s) is 10x slower than SSD reads (~5 GB/s), the CPU cannot keep up with I/O. LZ4 decompresses at 3-5 GB/s, close to SSD throughput. Yes, LZ4's 2x ratio uses more disk than gzip's 3x, but reads are an order of magnitude faster — critical for online queries. RocksDB uses tiered compression: LZ4 for hot L0-L1, zstd for cold L5-L6. Cassandra takes a similar approach. The lesson: what matters is pipeline throughput, not isolated ratio.

Q6. Why is Brotli better than Zstd for HTTP?

A. Static built-in dictionary plus HTML/CSS/JS tuning. Brotli ships a 120 KB built-in dictionary of English words, HTML tags, CSS properties, and JavaScript keywords. Web pages effectively get a huge virtual prefix, so small pages compress very well. Without a dictionary, Zstd lags slightly on web content. Benchmarks: on 10 MB of web content, brotli -11 beats zstd -19 by 5-10%. Brotli's context modeling (using nearby context to select the Huffman tree) is also more sophisticated. The trade-off is slow compression — Brotli is typically used for build-time precompression (*.html.br files), with runtime workloads going to zstd. Modern web stacks: static assets precompressed with Brotli; dynamic responses compressed with gzip or zstd; Cloudflare-style CDNs manage both at the edge.

Q7. Why does recompressing an already-compressed file (JPEG, MP3, ZIP) save almost nothing?

A. Compressed data is statistically near-random. Compression feeds on "redundancy plus bias"; well-compressed data, by definition, has almost none of either. LZ77 finds no back-references (every subsequence is unique), Huffman/ANS find no bias (every symbol is nearly equiprobable). The data already sits near Shannon's bound. Recompression yields (1) ~0-1% savings (noise), (2) small growth due to metadata, (3) wasted CPU. Exceptions: (a) redundancy between compressed files — archiving many copies of the same compressed file into a solid tar can still help, (b) lossy -> lossless re-encoding (e.g. JPEG to PNG) — different data. Operational rule: skip HTTP compression for already-compressed MIME types (image/jpeg, video/mp4, application/zip), which is why Nginx's gzip_types excludes them. Compression eats redundancy — on randomness, it starves.


19. Learning resources

Papers:

  • Lempel & Ziv, "A Universal Algorithm for Sequential Data Compression" (1977).
  • Huffman, "A Method for the Construction of Minimum-Redundancy Codes" (1952).
  • Duda, "Asymmetric Numeral Systems" (2009).
  • Yann Collet's Zstd blog posts.

Books:

  • "Introduction to Data Compression" — Khalid Sayood.
  • "Managing Gigabytes" — Witten, Moffat, Bell.

Blogs:

Tools:

Source:

  • Facebook/zstd on GitHub.
  • LZ4 on GitHub.
  • Brotli on GitHub.

If this post helped, check out these related ones:

  • "SIMD / AVX / NEON Deep Dive" — acceleration techniques used by compressors.
  • "RocksDB and LSM-Tree" — DB engines built on LZ4/Zstd.
  • "CDN and Edge Caching" — deploying web compression.
  • "Binary Serialization (Protobuf/Thrift/Avro)" — pre-compacted data formats.