效能比較
在 1.83 MiB 的代表性語料上對 lha、
tar、tar.gz、zip 做
壓縮與解壓縮比較,每個工具都跑了預設與最佳兩級。
數字清楚地說明了每個工具在哪一項領先。
每格 5 次取中位數,附樣本標準差。機器:Apple M 系列, 原生 lha 編譯產物(LHa for UNIX 1.14i-ac20220213,預設 configure 選項)。Wall-clock 端到端:每格的 compress + decompress 總時間。
一眼看完的表
同一 1.83 MiB 語料,壓縮解壓各 5 次,取中位數。標準差反映 筆記型電腦本身逐次運行的雜訊。
| 工具 | 級別 | compress | decompress | size B | 壓縮率 | ||
|---|---|---|---|---|---|---|---|
| 中位 | ±σ | 中位 | ±σ | ||||
| lha | -lh5- (預設) | ~50 ms | ±5 | ~40 ms | ±1 | 547,045 | 29.93 % |
| lha | -lh7- | ~46 ms | ±1 | ~40 ms | ±6 | 547,045 | 29.93 % |
| tar | 無壓縮 | ~80 ms | ±4 | ~82 ms | ±5 | 2,732,544 | 149.55 % |
| tar.gz | -6 (預設) | ~210 ms | ±5 | ~5 ms | ±0.1 | 455,435 | 24.93 % |
| tar.gz | -9 (最佳) | ~210 ms | ±1 | ~5 ms | ±0.1 | 455,435 | 24.93 % |
| gzip -c | -6 (原始流) | ~170 ms | ±4 | ~5 ms | ±0.2 | 455,486 | 24.93 % |
| gzip -c | -9 (最佳) | ~170 ms | ±3 | ~5 ms | ±0.2 | 455,486 | 24.93 % |
| zip | -6 (預設) | ~62 ms | ±1 | ~3 ms | ±0.4 | 566,683 | 31.01 % |
| zip | -9 (最佳) | ~85 ms | ±1 | ~3 ms | ±0.1 | 566,683 | 31.01 % |
所有時間均為 wall-clock 端到端。「級別」是各工具自帶的
壓縮段位。語料檔案數 304,未壓縮總位元組 1,827,165。
tar (無壓縮)的壓縮率超過 100% 是因為 tar
儲存每個檔案的 metadata(UID、GID、mode 位元、512 位元組
每記錄 padding);對零碎小檔案來說,沒經過 deflate 之前
就已經多了 50%。
怎麼讀這張表
1. compress 速度:zip < lha < gzip < tar < tar.gz
在預設級別下:
zip -6是壓縮最快的(約 62 ms)— deflate、不帶 tar 阻塞、每個檔案少量開銷。lha -lh5-排第二(約 50 ms),比 zip 慢 22% 左右。gzip -6 -c處理原始 tar 流需要 約 170 ms — deflate 即使在預設級別上也是比 LZHUF 複雜得多的編碼器。tar -czf在底層 deflate 之上 又多出 ~40 ms 的 framing(每筆記錄、512 位元組對齊 padding)。計 ~210 ms。tar -cf(無壓縮)約 80 ms — 只有 header 開銷,沒有 deflate。
在預設級別上,zip比 lha快 22%,
lha比 tar.gz快 76%。
compress 速度的相對排序在不同語料上穩定。
2. decompress 速度:tar.gz ≈ zip < lha < tar
decompress 方面速度排序很有趣:
zip與tar.gz的 decompress 是 3-5 ms — deflate 解碼成本低。lhadecompress 約 40 ms — LZHUF 的解碼 是位元串列的:每個位元都依賴前一個位元的表查詢。tardecompress 約 82 ms — 同一個 512 位元組記錄 padding 成本在出口側。
在 CDN 上解壓 100 次的真實情境裡,絕對 decompress 時間被 網路延遲主導。三種工具的 decompress 時間都在個位數毫秒級; 差異遠低於磁碟 seek + HTTP 請求的雜訊底限。
3. 壓縮率:tar.gz < gzip ≈ lha < zip
同一個 kenlm 語料上:
tar.gz -6與gzip -6 -c都達到 455 KB / 24.93% — deflate 在本語料上的 下界。-9沒找到更多優化。lha -lh5-約 547 KB / 29.93% — 比 deflate 大約 20%,換來 ~2.2 倍的壓縮速度。這是 LZHUF 在 1990 年設計時就做出的取捨:針對 "286 + 1 MB RAM 上快速解壓",而不是體積最優。zip -6約 567 KB / 31.01% — 因為 zip 的 deflate 設定較舊,DEFLATE64(寬視窗變體) 預設關閉。tar (無壓縮)2.7 MB / 150% — 記錄 header + 512 位元組 padding 讓小檔案膨脹一倍多。
tar.gz 與 lha 的差距約 5 個百分點
— 在 1.8 MiB 上就是約 91 KB。在快速 CDN 上重傳
這 91 KB 不到 100 ms;在慢速 CDN 上要 1-2 秒。這就是
實際網路線上的 size-vs-speed 權衡。
4. -9 在本語料上是個坑
對於已經具有相當冗餘性的文字(原始碼裡有大量重複的 關鍵字和空白),gzip -9 和 tar.gz -9 都比 -6 找不出 任何新的優化。語料豐富度不夠讓更高等級發揮作用;-9 在 compress 時間上貴了 5-10 倍,但壓縮率提升 ≈ 0%。
如果你有真正高熵的內容(影片片段、資料庫 dump、加密資料), -9 會拉開有意義的差距。本語料不滿足這個條件。
什麼場景用什麼工具
| 如果你關心的是… | 選 | 原因 |
|---|---|---|
| 最小可能的輸出 | tar.gz -9 / zstd / brotli |
預設 level 9(或 zstd -19 / brotli -11)是從單檔案
工具中能得到的最優壓縮率。代價是 compress 時間
比 lha 慢 5 倍多,壓縮率只差 5%。 |
| 最快的 compress | zip -6 |
約 62 ms。DEFLATE64 關閉,沒有 tar blocking, 每檔案少量開銷。比 deflate -6 損失 6% 壓縮率。 |
| 最快的 decompress(CDN、OTA、鏡像) | lha 或 zip |
這兩種工具在本語料上都在個位數毫秒級。lha 產物略大, 但解壓後的代碼體積本來就小;網路延遲主導。如果還關心 "無 zlib、無 libiconv 依賴",lha 才是答案。 |
| CI 產物 + CDN 的單檔案靜態二進位 | lha |
x eget use lha 把 152 KB
的完全靜態二進位檔裝到 ~/.local/bin,零依賴。
tar.gz需要 libz + libiconv + tar;
zip需要 libzip + libz。lha 沒這棵樹。 |
| Windows / macOS 使用者開箱即用 | zip(或 .tar.gz) |
zip 在 Windows + macOS 上是系統組件;tar.gz 也
原生開啟。lha 需要先裝二進位(x eget use lha
一行解決,但仍然是一個步驟)。 |
| 嵌入式 / 韌體 / 舊硬體 | lha |
在受限目標上解壓器 < 16 KB。設計目標是 640 KB RAM 的 MS-DOS 機器。 |
深入閱讀
為什麼 lha -lh5- compress 比 tar.gz -6 快
LZHUF(.lh5- 背後的演算法)使用 1990 年設計的
靜態 Huffman 表 + 快速字串搜尋方式。
deflate(gzip / zlib)使用 LZ77 + 動態 Huffman +
滑動視窗,編碼器需要在滑動視窗裡做選擇。視窗還沒填滿的
輸入(我們的 1.8 MiB 語料放 32 KB 視窗裡還有富餘)下,
deflate 大量時間在做匹配掃描;LZHUF 不需要。
decompress 側兩者幾乎打平 — 解碼迴路結構差不多。
在比 deflate 視窗大得多的語料上(-6 的 32 KB),deflate
會因為能找更遠距離的匹配而拉開壓縮率優勢。100 MiB 語料的話,
tar.gz -9 估計會以比 lha -lh5- 多 3-5 倍
的 compress 時間拉開壓縮率。
為什麼 tar (無壓縮) 比 input 大那麼多
tar 記錄每個檔案的 metadata(UID、GID、mode、時間戳、檔名) 加上 512 位元組/記錄 padding。我們 1.8 MiB / 304 個小檔案 的語料裡,metadata 占 50% 多。tar -czf 透過 LZ77 後端能 把大部分壓回去,但原始tar 對小檔案語料就是 比 input 大。
為什麼 zip -6 decompress 比 tar.gz -6 快
zip 的存檔格式在檔案結尾有一個 central directory —
解碼器可以非串流式地讀它。tar.gz 必須對
每筆記錄掃 tar header 來找 size,然後讓 deflate 解碼器填
那個 exact size。zip 的 deflate 可以 wrap 整個 central directory;
tar.gz 的 deflate 是 per-record 的,每筆記錄要單獨解析
header。差距在檔案數變多時拉大。
為什麼 lha 的 decompress 比 deflate / zstd / lz4 慢?
decompress 是 LZHUF(.lh5-、.lh7- 背後
的演算法)顯出年代感的場合。1990 年設計是位元串列
(bit-serial)的:
- 靜態 Huffman 表・位元單位解碼。
位元串流 1 個位元一個位元地讀,每個位元決定下一個
表參照。這是
src/huf.c裡的主要解碼迴圈 — 這個迴圈沒有平行性:bit n+1 必須在 bit n 的表參照後才能讀。 - LZ77 反向參照序列化解。解碼器讀到一個
length/distance 配對時,從 distance 前的位置
複製 length 個位元組。
src/slide.c的 滑動視窗以位元組為單位更新,沒有 SIMD memmove。 1.83 MiB 語料上,這個複製部分支配了執行時間。 - 原始碼完全沒有 SIMD。
grep -nE '(mmx|sse|avx|simd|__m128i|__builtin_ia32)' src/*.c零命中。vendored 1.14i 程式碼是 1995-2003 年的 純 ANSI C,無架構特定向量化。
也就是這個問題的答案是:演算法本身就是上限,而非實作。 LZHUF 在 1990 年針對「286 + 1 MiB RAM 上快速解壓」做 調校,位元串列解碼迴圈就是天然的解。給 lha 加 SIMD 優化 估計在 decompress 上能拿到 1.5-2 倍(length-distance 複製 是唯一能向量化的區塊),但底層的位元串列 Huffman 迴圈是 根本地序列。為更快的解壓,必須更換演算法, LZHUF 的顯微最佳化不夠。
比較:zstd decompress 比 lha 在同等壓縮率下
快約 5-10 倍,lz4 還能再快 3-5 倍。兩者
都以現代 SIMD CPU(x86_64 上的 SSE2/AVX2、
aarch64 上的 NEON)為標的調校,decompress 迴圈是
SIMD 友善地設計。lha 比 MMX(1996)早六年,最初的
維護者沒有可打的向量單元。
這在何時重要:lha 是「zstd/lz4 沒裝的系統上能跑的單檔案 靜態二進位」的正解。lha 不是需要微秒級 decompress 的高吞吐 CDN 的正解。
怎麼重現
在任何 Unix-like 主機上(macOS、Linux、BSD):
git clone --depth 1 https://github.com/ljh-sh/lha.git
cd lha
bash scripts/build.sh
cp build/src/lha /usr/local/bin/
# 語料
test -d corpus/kenlm_src || cp -R ../kenlm/upstream/kenlm corpus/kenlm_src
# 每工具 5 次取中位數。表中的數字來自 5 次(median ± sample-stdev),
# Apple M 系列筆電。
for tool in \
'lha :lha c k.lh5- corpus/kenlm_src' \
'tar :tar -cf k.tar -C corpus kenlm_src' \
'tgz6 :tar -czf k6.tgz -C corpus kenlm_src' \
'tgz9 :tar -czf -9 k9.tgz -C corpus kenlm_src' \
'zip6 :zip -qr -6 k6.zip kenlm_src' \
'zip9 :zip -qr -9 k9.zip kenlm_src' ; do
label=${tool%%:*}; cmd=${tool#*:}
rm -f k.*
for r in 1 2 3 4 5; do
rm -rf corpus/kenlm_src; cp -R ../kenlm corpus/kenlm_src
t=$( { time $cmd >/dev/null 2>&1; } 2>&1 | awk '/real/{print $NF}')
sz=$(stat -c%s k.* 2>/dev/null | head -1)
echo "$label t=$t sz=$sz"
done
done
數字會因機器而異(不同 CPU 之間差約 10%,跨 macOS / Linux 差
約 5%),但 compress 速度的相對排序
(zip → lha → tar → tar.gz)、decompress
(tar.gz → zip → lha → tar)、壓縮率
(tar.gz → lha → zip → tar) 是穩定的。