Phần 6 cho thấy chỉnh mức nén trong một thuật toán tuân theo lợi ích giảm dần. Nhưng còn một lựa chọn lớn hơn: chọn thuật toán nào? gzip, bzip2, xz, zstd — bốn công cụ có mặt trên gần như mọi máy Linux, và chúng khác nhau về bản chất, không chỉ về tốc độ. Chọn sai không chỉ tốn thời gian mà còn tốn dung lượng hoặc làm nghẽn đường ống. Câu trả lời hay bị nói một câu ("xz nén tốt nhất, zstd nhanh nhất") — nhưng thực tế tinh tế hơn, và phụ thuộc dữ liệu. Bài này (phần 7 loạt Nén) nén cùng một file 25MB bằng cả bốn công cụ và đo đủ bốn số — kích thước, tỉ lệ, thời gian nén và thời gian giải nén — để bạn thấy bức tranh thật.

Bốn triết lý nén

Cả bốn đều là nén không-mất-mát, nhưng dùng lõi thuật toán khác nhau:

  • gzip = DEFLATE = LZ77 + Huffman (phần 5). Cũ, đơn giản, tương thích rộng nhất — mọi nơi đọc được .gz.
  • bzip2 = BWT + MTF + Huffman. Dùng Burrows-Wheeler Transform sắp xếp lại dữ liệu để gom các ký tự giống nhau lại gần (tạo run), rồi Move-To-Front + Huffman. Rất hợp dữ liệu lặp nhiều.
  • xz = LZMA = LZ với cửa sổ rất lớn + range coding (một dạng arithmetic coding). Nén chặt nhất cho phần lớn dữ liệu, nhưng nén rất chậm.
  • zstd = LZ + FSE/ANS (+ dictionary tùy chọn). Thiết kế hiện đại (Facebook, 2016): nén nhanh, và đặc biệt giải nén cực nhanh bất kể mức nén.

Ảnh chụp đoạn mã nền tối minh hoạ gzip vs bzip2 vs xz vs zstd bốn triết lý nén khác nhau cùng một file 25MB đo tỉ lệ và cả tốc độ nén lẫn giải nén, bốn thuật toán bốn cách tiếp cận gzip bằng DEFLATE bằng LZ77 cộng Huffman quen thuộc khắp nơi bzip2 bằng BWT cộng MTF cộng Huffman biến đổi Burrows-Wheeler tạo run rồi nén xz bằng LZMA bằng LZ cửa sổ lớn cộng range coding nén chặt zstd bằng LZ cộng FSE ANS cộng từ điển tùy chọn nhanh hiện đại cùng mục đích nén không mất mát nhưng khác hẳn về tốc độ vs tỉ lệ, điểm mạnh mỗi cái trên lý thuyết gzip cân bằng tương thích rộng nhất mọi nơi đọc được gz bzip2 BWT rất hợp dữ liệu lặp nhiều text log nhưng chậm xz tỉ lệ nén chặt nhất cho phần lớn dữ liệu nhưng nén rất chậm zstd nén nhanh giải nén cực nhanh độc lập level có dictionary, đo cả bốn số không chỉ tỉ lệ run đo kích thước thời gian nén thời gian giải nén s bằng date nén e bằng date d0 bằng date giải nén d1 bằng date tỉ lệ nén không phải tất cả tốc độ giải nén quan trọng khi đọc nhiều lần, cạm bẫy cái nào nén chặt nhất phụ thuộc dữ liệu thường nói xz nén chặt nhất nhưng trên dữ liệu lặp nhiều log BWT của bzip2 có thể thắng kết quả phụ thuộc đặc điểm dữ liệu đo thật

Hình 1: Bốn thuật toán, bốn lõi — gzip (DEFLATE = LZ77+Huffman), bzip2 (BWT+MTF+Huffman), xz (LZMA = LZ cửa sổ lớn + range coding), zstd (LZ + FSE/ANS + dictionary); phải đo cả bốn số (size, ratio, thời gian nén, thời gian giải nén) chứ không chỉ tỉ lệ.

Đo thật: cùng file 25MB, bốn công cụ

Mình tạo một file log 25MB (có cấu trúc như log thật), nén bằng cả bốn công cụ ở mức mặc định và mức cao, đo cả bốn số:

Ảnh chụp bảng kết quả chạy thật so sánh bốn công cụ nén output thật go-lab 24981034 byte log gzip bzip2 xz zstd CLI, bảng so bốn công cụ gốc bằng 24981034 byte công cụ size B ratio nén ms giải ms gzip -6 3347632 7.5x 258 61 cân bằng quen thuộc bzip2 -9 1874227 13.3x 1486 338 nén chặt nhất ở đây xz -6 2350060 10.6x 7028 94 nén rất chậm zstd -3 3771854 6.6x 61 15 nén nhanh nhất zstd -19 2393346 10.4x 8009 12 giải nhanh nhất, đọc bảng mỗi công cụ vô địch một tiêu chí khác nhau zstd -3 nén 61ms cộng giải 15ms bằng nhanh toàn diện realtime throughput zstd -19 giải nén 12ms nhanh nhất dù nén chậm giải độc lập level bzip2 -9 ratio 13.3x cao nhất ở đây BWT cực hợp log lặp nhiều xz -6 ratio 10.6x tốt nhưng nén 7 giây chỉ đáng khi nén 1 lần gzip -6 không vô địch gì nhưng đọc được khắp nơi mẫu số chung, lưu ý trung thực ratio phụ thuộc dữ liệu bzip2 thắng ratio ở đây vì log rất lặp BWT tỏa sáng trên mã nguồn dữ liệu tổng quát xz LZMA thường dẫn đầu ratio đo trên dữ liệu của bạn

Hình 2: Chạy thật trên 24.981.034 byte log — gzip -6: 3347KB/7.5x, nén 258ms, giải 61ms; bzip2 -9: 1874KB/13.3x (chặt nhất), nén 1486ms, giải 338ms; xz -6: 2350KB/10.6x, nén 7028ms, giải 94ms; zstd -3: 3771KB/6.6x, nén 61ms, giải 15ms; zstd -19: 2393KB/10.4x, nén 8009ms, giải 12ms.

  • zstd nhanh toàn diện: zstd -3 nén 25MB trong 61ms và giải nén trong 15ms — nhanh nhất cả hai chiều. Với hệ thống throughput cao (nén log đang chảy, dữ liệu qua mạng), đây là lựa chọn hiển nhiên: nó gần như "miễn phí" về CPU.
  • zstd giải nén cực nhanh bất kể mức nén: điểm đặc biệt nhất của zstd. zstd -19 nén chậm (8009ms) nhưng giải nén chỉ 12ms — thậm chí nhanh hơn cả zstd -3. Tốc độ giải nén của zstd gần như độc lập với mức nén. Đây là tính chất vàng cho "nén một lần, đọc nhiều lần": dùng -19 để nén asset một lần, mọi lượt tải sau giải nén siêu nhanh.
  • bzip2 nén chặt nhất — nhưng trên dữ liệu này: bzip2 -9 cho tỉ lệ 13.3x — cao nhất trong bốn công cụ ở đây. Lý do: BWT cực kỳ hợp dữ liệu lặp nhiều như log (nó sắp xếp lại để gom run). Đây là một bài học trung thực: câu "xz nén chặt nhất" không phải luôn đúng — trên log lặp nhiều, BWT của bzip2 thắng. Trên mã nguồn hay dữ liệu tổng quát, xz/LZMA thường dẫn đầu. Kết quả phụ thuộc đặc điểm dữ liệu — phải đo thật.
  • xz: chặt nhưng nén chậm khủng khiếp: xz -6 cho 10.6x nhưng mất 7028ms để nén (chậm gấp ~115 lần zstd -3). Tuy nhiên nó giải nén khá nhanh (94ms). xz chỉ đáng khi bạn nén một lần và dung lượng quan trọng hơn thời gian nén (bản phát hành phần mềm, gói cài đặt — đó là lý do nhiều distro Linux dùng .tar.xz).
  • gzip: không vô địch gì, nhưng là mẫu số chung: gzip không thắng tiêu chí nào, nhưng nó đọc được ở khắp mọi nơi — mọi ngôn ngữ, mọi công cụ, mọi trình duyệt hiểu gzip. Khi tính tương thích quan trọng hơn hiệu năng (HTTP Content-Encoding, trao đổi dữ liệu công khai), gzip vẫn là lựa chọn an toàn.

Đánh đổi cần cân nhắc

Không có "cái tốt nhất" — chỉ có "tốt nhất cho ràng buộc của bạn". Bốn số (tỉ lệ, thời gian nén, thời gian giải nén, tính tương thích) tạo một không gian đánh đổi, và mỗi tình huống ưu tiên khác nhau. Quy tắc thực dụng: throughput/realtime → zstd (mức thấp); nén một lần đọc nhiều lần, cần nhỏ → zstd -19 (giải nhanh) hoặc xz (nếu cần nhỏ tối đa và có công cụ); dữ liệu lặp cực nhiều → thử bzip2; cần ai cũng đọc được → gzip.

zstd đang thay thế cả ba trong nhiều hệ thống — vì đường cong đánh đổi tốt hơn. Điều mà bảng trên gợi ý: zstd cho cả tốc độ tốt và tỉ lệ cạnh tranh — nó "đẩy lùi biên Pareto". zstd -19 đạt tỉ lệ gần xz mà giải nén nhanh hơn nhiều. Đó là lý do Linux kernel, Facebook, nhiều database và filesystem (btrfs, ZFS) chuyển sang zstd. Nếu bạn được tự do chọn và không cần tương thích gzip, zstd thường là mặc định tốt nhất năm nay.

Đo trên dữ liệu của bạn — bài này đã chứng minh vì sao. Kết quả "bzip2 thắng ratio" ở đây trái với trực giác thông thường, chỉ vì log rất lặp. Dữ liệu của bạn (JSON, ảnh đã nén, nhị phân, text tiếng Việt) sẽ cho bảng khác. Đừng chép kết luận của người khác — chạy time bốn công cụ trên mẫu thật (vài phút) rồi quyết. Đây là cùng nguyên tắc "đo trước khi tối ưu" xuyên suốt.

Ba ý mang về

  1. Bốn thuật toán, bốn triết lý: đo thật trên 25MB log — gzip (DEFLATE, tương thích rộng), bzip2 (BWT, chặt nhất ở đây 13.3x nhờ dữ liệu lặp), xz (LZMA, nén chậm 7 giây), zstd (LZ+ANS, nhanh toàn diện).
  2. zstd giải nén cực nhanh bất kể mức nén: đo thật zstd -19 nén chậm (8s) nhưng giải nén chỉ 12ms — nhanh hơn cả zstd -3; tính chất vàng cho "nén một lần đọc nhiều lần", lý do zstd đang thay thế cả ba trong nhiều hệ thống.
  3. "Cái nào tốt nhất" phụ thuộc ràng buộc VÀ dữ liệu: đo thật bzip2 thắng ratio trên log lặp (trái trực giác "xz chặt nhất"); chọn theo throughput/kích thước/tương thích, và luôn đo bốn số trên dữ liệu thật của bạn.

Nguồn

Phần sau ta khai thác một tính năng riêng của zstd cứu vãn trường hợp khó nhất: nén dữ liệu nhỏ bằng từ điển (dictionary) — vì sao nén nghìn bản ghi JSON nhỏ riêng lẻ lại tệ, và dictionary sửa nó thế nào, đo thật.