演算法 — 歷史、效能、當前定位
lha 攜帶的是 LZHUF:
1990 年代的 LZ77 + 動態 Huffman,為「1 MiB 記憶體的 286
上的快速解壓縮」而設計,寫在 SIMD 還沒出現的年代。本
頁說的是這個演算法、它在 2026 年付出的代價、以及為什
麼一份外部專案的單一靜態二進位檔,仍然會以它為
底座。
1. 演算法
lha 家族裡每一種壓縮方式(-lh0- 到
-lh7-,外加 -lhd- 目錄標頭)都
是同一個兩段式結構:
- LZ77 風格的字串比對。在輸入裡找重複的位元
組序列,並發出形如
(distance, length)的回引用,代替重複位元組。所有 LZARI / LZHUF / ZIP / gzip / zstd / lz4 / 7-zip 裡的「LZ」都是同一 個想法,源頭是 Lempel & Ziv 1977 年的論文。 - 熵編碼器,作用在字面值與回引用上。這是各家
不同的部分:
-lh1-— LZARI: LZ77 + 算術編碼(Okumura,1988)。-lh5-— LZHUF: LZ77 + 動態 Huffman(Tagawa,1990)。預設方式。-lh4-、-lh6-、-lh7-、-lzs-— 熵後端的實驗性改良,或為相容性而保留的未使 用編碼。
靜態 vs 動態 Huffman
LZHUF 用的是靜態 Huffman 表,每個區塊重算一次, 輸入裡每幾 KB 重新推導一次。這個設計抉擇決定了整組取捨:
- 好處。編碼器不必把 Huffman 表隨位元流一起送出。 解碼器已知每個區塊的表,在一個緊湊的內層迴圈裡解 碼,不必在每個區塊開頭載入新表。
- 代價。當局部統計量變化(標頭區塊 —> 結構化文字主體 —> 二進位區塊)時,靜態表 是局部次優的。動態 Huffman(deflate 的預設, gzip 用的)把表跟資料一起攜帶並自適應。這就是 基準裡 ~5 % 壓縮率差距的來源。
位元串列解碼迴圈(以及為什麼它慢)
LZHUF 的解碼迴圈逐位元讀入位元流,每次透過一 個小型 2× 表查找下一個符號。這在本質上就是串列的: 位元 n+1 必須在位元 n 查表完成後才能讀。
ANSI C 源碼就是直白的 1995-2003 年程式碼,沒有 SIMD intrinsic:
$ grep -nEi '(mmx|sse|avx|simd|__m128i|__builtin_ia32)' upstream/lha/src/*.c
$ # 零命中
把 LZ77 的 length-distance 複製向量化,可以拿到大約 1.5–2× 的解壓縮速度;位元串列的 Huffman 迴圈就是 天花板。要在解壓縮上更快,必須換演算法,不能再微觀最佳 化 LZHUF(更長的版本見 基準頁)。
2. 歷史 — 世系
首頁的歷史章節對 1988-2022 這段弧線講得很詳細;緊湊版:
- 1977 — LZ77。Abraham Lempel 與 Jacob Ziv 在 IEEE Transactions on Information Theory 發 表 A Universal Algorithm for Sequential Data Compression。滑動視窗 + 回引用這個思路,定義了 之後每一種現代歸檔器。
- 1988 — LZARI。奧村晴彥發表對 CPU 友好的 LZ77 + 算術編碼變體。LHA 借用的、面向 286 的算術 編碼器。
- 1988–1989 — LHarc。吉崎英明 (Y.Tagawa)把 LZARI 包成 MS-DOS 上的封存格式。日 本第一種廣泛使用的封存格式。
- 1990 — LZHUF,本專案分發的格式。Tagawa 把
LZARI 改良為 LZ77 + 動態 Huffman,把格式從 LHarc 改
名為 LHA。壓縮方式後綴的慣例(封存檔名裡的
-lh5-)就此誕生,並沿用至今。 - 1991–1993 — LHA for UNIX。大木優移 植到 UNIX;綿貫和彥重寫程式碼庫。這份 canonical C 源碼,30 年來一直被 vendored。
- 1993 — PKZIP 2.0 + Info-ZIP在西方 PC 上 取代 LHA。
- 1994–2010 —
.lzh在日本仍然火。 主要網站用 LZH 分發;Windows 自解壓大量使用 LHA; 2ch 風格的電子佈告欄交換 LZH 一直延續到 2000 年代 後期。.lzh是日本 2000 年代初之前主流 的分發格式。 - 2022 — autoconf 復活。GitHub 使用者
jca02266
接手沉寂的程式碼樹,加入現代 autotools 建置,打出
LHa for UNIX 1.14i-ac20220213 標籤。這就是本倉
儲在 commit
86094cb處 vendored 的來源。 - 2026 — ljh-sh/lha。Vendors
jca02266/lha@86094cb;為每個主流平台產 出單一靜態二進位檔。今天還存在的理由在 §4。
3. 效能 — 三條軸
基準資料來自 1.83 MiB 的 kenlm 原始 碼語料,每個 cell 跑 5 次,取中位數 ± 樣本標準差。要點:
| 工具 | 等級 | 壓縮 | 解壓縮 | 壓縮率 |
|---|---|---|---|---|
| lha | -lh5-(預設) |
~50 ms | ~40 ms | 29.93 % |
| zip | -6(預設) |
~62 ms | ~3 ms | 31.01 % |
| tar(不壓縮) | — | ~80 ms | ~82 ms | 149.55 % |
| tar.gz | -6(預設) |
~210 ms | ~5 ms | 24.93 % |
三條值得記的觀察
- 壓縮速度。
lha排在zip(最快,領先 ~22 %)和tar.gz(最慢,慢 ~4×)之間。對於「壓縮 後留存」類型負載(建置產物留存、鏡像站),lha是非平凡壓縮器裡最便宜的,仍然比裸流傳得快 ~3×。 - 解壓縮速度。
zip和tar.gz解壓縮在個位數毫秒;lha要 ~40 ms。位元串列 Huffman 迴圈就是天花板。絕對時 間仍然很小 — 遠低於任何 CDN 的網路 RTT 雜訊。 在 CDN 上從鏡像重新解壓縮 100 次的工作負載,被 HTTP 延遲支配,而不是演算法選擇。 - 壓縮率。
lha比deflate高 ~5 個百分點(1.8 MiB 源碼上多 ~91 KB)。這是 1990 年的刻意取捨:調校目標是 286 上解壓縮快,而不是 2020 年工作站上壓得最緊。
哪裡輸給現代演算法 — 哪裡不輸
- ✗ 壓縮率上,
deflate與zstd贏 5–15 個百分點。 - ✗ 解壓縮速度上,
zstd比lha快 ~5–10×;lz4再快 ~3–5×。 - ✓ 靜態二進位檔體積 + 零相依性分發上,
lha完勝:~150 KB,在任何有核心的地方 都能跑。 - ✓ 「我手上有一個 1994 年遊戲的封存檔,其他都打不開」
上,
lha預設就贏 — 因為只有它能 打開。 - ✓ 長期封存穩定性上,
lha贏在它的 35 年格式規格今天還在被實作,還在保持讀相容。
基準頁的「深入」章節講演 算法內部原因。短版本:lha 是 1990 年的設計,到今天沒變過, 速度天花板就是演算法本身,不是實作。
4. 當前定位 — 為什麼這個專案在 2026 年還存在
被 vendored 的 jca02266/lha@86094cb 源碼,
是 1995-2003 年代的 ANSI C 程式碼庫,2022 年只經歷過一次
現代化(autotools)。ljh-sh/lha 從那裡出發只做一件事:
為每個平台產出一個 lha 的單一靜態二進位
檔,讓使用者不需要編譯器。
2026 年這個命題有三類具體需求情境:
4.1 老 .lzh 的解壓縮
最直觀的情況:某人手上有 1994 年的遊戲補丁、2ch 風格
的 BBS 歷史附件、或 2000 年代的日本開放原始碼封存檔。
現代工具都打不開 .lzh。裝上
x eget use lha,兩秒鐘就到
~/.local/bin,再
lha x foo.lzh 就能解。少了這個二進位檔,
使用者只能裝編譯器、找 1990 年代的 autoconf 補丁、然
後祈禱他們的 libc 還沒把這個建置搞壞。
4.2 單檔靜態二進位檔用於 CI 產物 / CDN 分發
lha 從任何源碼樹產出一個 .lzh
封存檔,耗時 ~50–80 ms,主機上零系統相依性。對
於 CI 產物發布者(任何把建置結果打包下載的人來說),意
思是:
- 不用把
libz、libcurl、libiconv、libssl拖進精簡 容器。 - 一個 ~150 KB 的二進位檔,簽名、分發。
- 解壓縮路徑在 Windows、macOS、Linux、BSD 和任何嵌入
式 Linux 上都成立 — 因為二進位檔從
~/.local/bin跑,不需要安裝到系統。
這就是 ljh-sh 在各專案裡都採用的「靜態二進位檔分發慣例」 (kenlm、upxz、現在的 lha)。具體管線可看 建置稽核。
4.3 x-cmd 的 zuz 模組 — 直接動機
這是本專案以靜態建置形式分發的根本理由。
x-cmd 的
zuz 模組對封存檔做格式自動辨識:按副檔名選工
具,透明地解壓縮或壓縮。它支援 tar、
tar.gz、tar.bz2、tar.xz、
zip、7z、rar、
lha 等。但它有一條架構鐵律:
絕不內嵌格式專用的工具。永遠呼叫系統已安裝的 二進位檔。
問題落在 x-cmd
issue #131(2024 年 12 月):某個使用者希望
zuz 支援 LHA/LZH,但 x-cmd 當前支援的所有
可攜式套件管理程式都不內建 lha。討論線索
如下:
- 2024 年 12 月:維護者(edwinjhlee)指出 v0.5.1 已
提供了基於 7z 的解壓縮回退(只要使用者裝
了
lha或lhasa就能跑)。 - 2025 年 1 月:維護者讓使用者用
x install lha作為權宜路徑,直到出現 打包的lha模組。 - 2026 年 7 月:維護者
收尾:
zuz將惰性下載本倉儲的 release 產物,這樣x uz foo.lha在任何預先沒 裝lha的系統上都能跑。
也就是說,zuz 使用者的主要安裝路徑是:
x uz foo.lha
# zuz 偵測到 .lha,發現 PATH 裡沒有 lha,
# 回退到:eget ljh-sh/lha --to ~/.local/bin/lha
# 然後:~/.local/bin/lha x foo.lha
不是 Homebrew。不是 apt。不是各發行版的套件。一個預 設的靜態安裝,不需要特權安裝、不需要 tap、不需要建 置鏈、在 x-cmd 支援的每個平台上都能用。本倉儲存在的全部 理由,就是為它提供這一份「預設靜態安裝能力」。
如果你是 x-cmd 使用者,在一台沒裝 lha 的系統
上 x uz foo.lha,你就是在用這個二進位檔。
如果你從來沒用過 x-cmd,只是要打開一個
.lzh,你也在用這個二進位檔 — 同一份
產物、同一個 release、同一個被 vendored 的源碼。
4.4 這個專案「不是」什麼
老實講清楚邊界:
- 不是演算法升級。沒有 tANS 移植、沒有 LZMA-lha 變體、沒有面向 LZH 的 tANS 格式。Vendored 的 1.14i 保持原樣。
- 不是
tar.gz、zip、zstd的替代品。基準 說得很清楚,何時該選哪個。 - 不是LZH 捲土重來的訊號。這個二進位檔存在的
唯一理由是格式本身的長壽;如果你的工作流是 2010
年之後開始的,你多半沒有
.lzh檔案。
5. 總結
想要一條經驗法則:
| 如果你在意… | 就選 |
|---|---|
打開一個 1994 年的 .lzh |
lha(本專案的二進位檔) |
| 封閉系統上的單一靜態二進位檔 | lha(~150 KB,零相依性) |
類似 zuz 的格式自動辨識 |
lha(惰性從 ljh-sh/lha 拉) |
| 長尾 CDN 上最小的產物 | tar.gz -9 或 zstd |
| 微秒級解壓縮 | lz4 或 zstd |
本頁存在的理由是:「這個二進位檔是什麼」和「為什麼它 存在」值得集中在一個地方,而不是被 歷史、 基準、建 置稽核、FAQ 各頁瓜分。