← 筆記

一個編譯旗標,快 20 倍

2026 年 8 月 4 日 · Recilic 開發筆記

第一個 mesh 跑起來的隔天早上,它正以 1.2 MB/s 把 61.9 GB 的照片庫送過房間。跑完要十四小時。到了早餐時間,它變成 23.8 MB/s——而真正有效的那個修法,說出來有點難為情。

MacBook 與 Mac mini 之間 23.8 MB/s 的 Live mesh

嫌疑犯名單

顯而易見的推論全都指向協定。每個分片都是嚴格一個一個寫:送出、等對方回應、才送下一個——吞吐量被往返延遲鎖死,跟頻寬無關。而 19,000 張照片幾乎每個檔案都只有一個區塊,每個分片都要付完整的一次往返。兩件事都是真的,都值得修。但都不是主角。

然後那個一句話破案的問題來了:「等一下,這兩台 Mac 該不會不是走區網,而是繞 VPN 出去吧?」要確認就得量原始鏈路,於是:在同樣兩台機器之間用 SSH 灌 100 MB。20 MB/s。網路完全沒問題。Recilic 只用了自己早就擁有的那條路的百分之六。

不光彩的答案

開發版是 Debug build。於是我把熱路徑在兩種配置下各跑一次基準測試,每個階段灌 8 MB:

糾刪碼編碼(我們的 Swift):3.2 MB/s → 142.9 MB/s  (44×)
ChaChaPoly 加密(CryptoKit):958.8 → 958.5 MB/s  (1.0×)
SHA-256(CryptoKit):3260 → 3156 MB/s  (1.0×)
Chunk 切塊(我們的 Swift):1965 → 2080 MB/s  (1.1×)

整個落差只存在於一個地方:GF(256) 糾刪碼編碼器。加密從來就不慢——CryptoKit 是系統內預先最佳化好的,你的建置設定傷不到它。但編碼器是我們自己的 Swift,每一個 byte 都要透過查找表做乘法。開了最佳化,gfMul 被 inline 進緊迴圈、Data 的引用計數流量消失、whole-module optimization 還能跨檔案 inline 查找表。在 -Onone 下,每個 byte 都要付一次真的函式呼叫加上引用計數。

為什麼一個編譯旗標值 44 倍

想像你要在 660 億個信封上蓋章。編碼器面對的差不多就是這件事:61.9 GB 是 660 億個 byte,而它要對每一個做四次乘法——總共幾千億次微小操作。每次操作本身都極其簡單:查兩個表、把結果合起來。真正決定你這個下午怎麼過的,是每次操作周邊的手續

Debug 模式是這樣做的:每蓋一個信封,走去倉庫拿印章、走回來、蓋下去、把印章還回倉庫、然後在本子上登記一筆。蓋章是一瞬間的事。走路才是全部。

Release 模式把印章握在手上,本子丟掉。

翻譯回術語:走去倉庫那趟路是函式呼叫,最佳化器會把它inline 直接展開在迴圈裡;本子是自動引用計數(ARC),最佳化器證明它沒必要就刪掉。兩者都沒有改變程式算出什麼——只改變了每個 byte 周圍要走多少儀式。

這也正是 Apple 的加密免疫的原因:它出廠時就是封好的,本來就處於「印章握在手上」的狀態,你的建置設定伸不進去。只有你自己寫的程式碼要繳 debug 稅——而且是每個 byte 繳一次。

而數字對得剛剛好:一個 3.2 MB/s 的編碼器,扣掉 I/O 和封裝開銷,就是我們看到的 1.2–2 MB/s。一個 142.9 MB/s 的編碼器遠高於 20 MB/s 的網路——所以瓶頸搬到了線路上,那本來就是它該待的地方。

開最佳化重編:1.2 → 23.8 MB/s,甚至高於我們用 SSH 量到的 20 MB/s。原本要十四小時的備份現在大約四十五分鐘。瓶頸回到 Wi-Fi 鏈路——它本來就該在那裡。

那些依然值得做的修法

協定層的推論並沒有錯,只是被更大的因素蓋過去了——而它們照樣上線了,因為到了滿速之後,剩下的延遲才真的開始有感:

分片並發寫入。一個區塊的六個分片現在平行寫入,不再排隊。任何一個失敗時,整個 group 仍會被 drain 完,讓每個已經落地的分片都進入清理名單——寫一半的備份絕不能留下孤兒。

連線池。每個節點四條通道,並發寫入的往返互相重疊,而不是全部塞在同一根管子後面排隊。

兩個從真實使用中冒出來的傷口。筆電傳輸中途睡著,過去會讓備份永遠掛在一個死掉的 socket 上——現在 TCP keepalive 加上每個請求的期限,一分鐘內就轉成「節點暫時無法連線」,下次備份自動重連。另外,App 啟動後的第一次連線可能會失敗一次,因為 macOS 正在重建它的區網權限快取;現在它會自己重試,所以沒人會看到。

順手修掉的一個 bug:前置條件缺失時按下「Back Up Now」——什麼都沒發生,而且無聲無息。一個看起來可以按、按下去卻只回你沈默的按鈕,比錯誤訊息更糟。現在它會明確說出缺什麼。

把教訓寫下來

絕對不要在 Debug build 上診斷效能問題。在怪自己的協定之前,先量原始鏈路——而當有人問出「我們到底有沒有走對網路?」這種聽起來很天真的問題時,就去量它。那個問題值 20 倍。

Recilic 0.1.0 已上架 Mac App Store,創始價 $9.99。mesh 會以免費更新推出——而且是滿速的。