Skip to content

Aster 比 Mihomo 快多少? ​

先看結論 ​

Aster 沒有用魔法把你的網路變快,而是把代理核心內大量重複的小工作拿掉。以下是在實際 OpenWrt 軟路由上,相同程式與測試各跑三輪的中位數:

你會遇到的工作Aster 快多少為什麼會快
TCP 搬運 32 KiB 資料吞吐約高 2%、處理時間約少 2%重複使用搬運資料的暫存空間
AnyTLS 打包 1 KiB約快 2.0 倍直接寫進可重用 buffer,不再多包一層物件
AnyTLS 打包 16 KiB約快 1.4 倍同上,並減少小型記憶體申請
準備一個 UDP 封包約快 5.4 倍封包資料結構用完收回,下次直接重用
忽略一行已關閉的 debug log約快 101 倍在組字串、建立 log 前就直接跳過

IMPORTANT

最接近「實際搬資料」的是 TCP 的約 2% 改善。 5.4 倍和 101 倍只代表核心裡某個很小、但會執行非常多次的步驟,不代表下載速度會直接變成 5.4 倍或 101 倍。

NOTE

最新的 2026-09-07 Linux WSL2 A/B 比較 Aster PR #4 前後,不是 Aster 對 Mihomo:4,096 筆 DNS 快取的 RSS 中位數降低 2.5%,但前後範圍重疊,1,000 條 TCP 沒有省 RAM。大型 pool 操作與完整 DNS 訊息快取命中反而較慢。下方保留全部結果與代價,不把它改寫成下載速度提升。

到底改了什麼? ​

1. 不再每個封包都申請新記憶體 ​

以前 TCP、UDP、AnyTLS 處理一次資料,就可能建立新的暫存物件。現在用完會收回,下個封包直接重用。結果是 CPU 少花時間整理垃圾記憶體,高負載時也比較不容易突然抖一下。

2. 多條連線不用搶同一把鎖 ​

以前封包查規則、找代理時,要讀取大家共用的資料並拿鎖。現在設定更新時先做好一份完整快照,處理封包只讀快照,不必互相排隊。

3. UDP 不再一直重做相同轉換 ​

來源位址直接用電腦容易比較的格式保存,不再每個封包都轉成字串。socket 的逾時計時也改成合併刷新,而不是每收到一包就重設一次。

4. AnyTLS 的規則先算一次 ​

Padding 規則在載入設定時就先解析好。真正傳資料時,Aster 只要查結果並直接組 frame,不再邊傳邊拆字串、重新計算。

5. 不會顯示的 log 就完全不做 ​

如果 debug log 已關閉,而且也沒有面板在監聽,Aster 會在格式化訊息前返回。以前即使最後不顯示,仍會先建立完整 log 再丟掉。

6. 流量數字直接累加 ​

上傳、下載與連線數改用便宜的增量統計。每次有流量時只更新數字,不再為了得到總數反覆掃描所有活動連線。

以下是完整測試數據 ​

2026-09-07 PR #4:Linux 回歸驗證與記憶體 A/B ​

比較更新前 be6d7912 → 最新核心 c5f553dc(PR #4:bounded allocator slabs、compact A/AAAA cache)。這是 Aster 對 Aster,不是 Mihomo 比較,也不是後續記憶體規劃已實作完成。

環境與方法 ​

  • Arch Linux/WSL2,kernel 6.6.114.1-microsoft-standard-WSL2;Ryzen 9 5900X、24 logical CPUs、WSL 可用約 31.3 GiB RAM。不是 OpenWrt,也不是隔離/固定頻率的實體 Linux 測試機。
  • Linux 工具鏈為 go1.26.5-X:nodwarf5;兩版相同,GOAMD64=v1、CGO_ENABLED=0、-trimpath -ldflags='-s -w'。整程序 GOMAXPROCS=2,microbenchmark 為 1。沒有設定 GOGC/GOMEMLIMIT,沒有強制 GC。
  • 四個情境各七輪,每輪使用新程序,before/after 交錯並輪替先後,共 56 個完整核心程序。每情境前後 YAML SHA256 相同。
  • Rule + MATCH,DIRECT、TUN 關閉、loopback SOCKS/controller。除 DNS 情境外 DNS 與 IPv6 關閉。controller 開 debug,但沒有連接 log WebSocket。
  • workload 建立後等待 15 秒,以 250 ms 間隔採樣三秒;每程序取中位數,再報七程序中位數及完整範圍。讀取 /proc/<pid>/status 的 VmRSS/VmHWM/VmSwap;所有採樣的 swap 均為 0。這些不是 Windows working set,也不包含所有 kernel socket/splice pipe 記憶體。
  • TCP 經 SOCKS 到獨立 Python loopback echo,先驗證每條 echo 與 controller 連線數;每條再上下行各 128 × 4096 bytes、最多 16 個 worker。關閉並確認 controller 歸零後等待 60 秒。client 綁 127.0.0.2,避免本機不同 TCP 四元組的 source AddrPort 重複而觸發 loop detector。
  • DNS 使用 LRU、容量 8192,注入 4096 個不同名稱(A/AAAA 各半、TTL 3600)後重查一次。每個程序確認第一次恰有 4096 次 loopback upstream 查詢,第二次為 0;未靠縮短 TTL 或降低容量換數字。

完整程序 RSS ​

單位 MiB;括號為七輪完整範圍。原始資料另含各階段 VmHWM 與 12 個採樣點。

情境Before RSS(範圍)After RSS(範圍)中位數變化
空載35.11(34.30–38.80)34.82(34.13–38.80)−0.8%
持有 100 條 TCP36.05(35.76–36.34)35.71(35.42–36.05)−1.0%
100 條 TCP 釋放後 60 秒36.28(35.88–36.34)35.85(35.42–36.19)−1.2%
持有 1,000 條 TCP58.35(57.32–61.21)60.93(57.49–61.90)+4.4%
1,000 條 TCP 釋放後 60 秒59.42(58.52–64.46)63.93(58.69–65.01)+7.6%
4,096 筆 A/AAAA DNS cache44.19(43.75–47.04)43.09(42.63–45.90)−2.5%

所有前後範圍均重疊。 DNS 中位數少約 1.10 MiB,但不能承諾固定省 RAM;TCP 1,000 條的中位數反而上升。兩版釋放後均未回到空載。流量完成且仍持有 TCP 時,RSS 中位數與持有階段相同,完整範圍見 evidence。這輪沒有證明 bounded pool 讓整程序在 60 秒內把 RAM 還給 OS。

CPU/配置代價:不是全面加速 ​

每 case/sample 都由 go test -c 的 binary 啟動新程序,兩版 benchmark 函式相同;每項 2 秒、-test.cpu=1、七輪交錯,共 112 個 microbenchmark 程序。表格為中位數;完整 samples、範圍與 benchstat 在證據包。

工作Before → AfterB/op;allocs/op:前 → 後
allocator 8 KiB Get/Put15.35 → 15.29 ns(未顯著)0;0 → 0;0
allocator 16 KiB Get/Put15.72 → 36.66 ns(+133.2%)0;0 → 0;0
allocator 32 KiB Get/Put15.59 → 37.28 ns(+139.1%)0;0 → 0;0
allocator 128 KiB Get/Put15.38 → 37.32 ns(+142.7%)0;0 → 0;0
DNS GetMsgFromCacheHit199.1 → 397.6 ns(+99.7%)252;5 → 516;10
DNS ExchangeContextCacheHit345.0 → 529.0 ns(+53.3%)252;5 → 516;10
DNS LookupIPv4CacheHit200.9 → 203.8 ns(未顯著)88;3 → 88;3
relay 32 KiB4.302 → 4.252 µs(未顯著)0;0 → 0;0

大型 channel pool 用較高的 Get/Put 成本換容量上限;完整 DNS 訊息讀取需要重新展開 compact entry,這輪量到明確的時間與配置回退。上述五個時間回退的 benchstat p=0.001(未作多重比較校正);IPv4 lookup p=0.805、relay p=0.902,不宣稱這兩者變快。這是已合併版本的實測報告,沒有偷偷變更核心或掩蓋回退。

Heap 診斷的限制 ​

另開六個診斷程序,對空載/TCP 1,000/DNS 各取前後 inuse_space,呼叫 /debug/pprof/heap?gc=0;不在 RSS cohort 內抓 profile,避免診斷配置污染後續階段。

  • DNS before 的 flat top 3:protobuf RegisterFile.func2 514.38 KiB、regexp compiler 513.50 KiB、dns.cloneMsg 512.07 KiB;after:gopacket init 525.43 KiB、protobuf type registry 513.12 KiB、dns.compactIPs 512.04 KiB。
  • TCP before 只有兩個非零 flat 項:regexp2 init 518.65 KiB、DNS map init 512.88 KiB;after 為 regexp.onePassCopy 521.05 KiB、sync.OnceValue 512.01 KiB。沒有第三個非零項可報,也不能據此算每條 TCP 的 heap bytes。
  • 自然 GC 與預設抽樣下,heap profile 可能落後當下配置;此輪 TCP profile 主要仍是初始化資料,不足以完成規劃書 P0-1 的歸因驗收。原始 profile/top 輸出保留,不以強制 GC 美化結果。

驗證與證據 ​

  • Windows Go 1.26.3:一般/with_low_memory 全套測試與建置、pool/DNS 兩模式 race、go vet ./...、變更 Go 檔案 gofmt 與 golangci-lint(0 issues)通過。
  • Linux Go 1.26.5:go test -p=4 -count=1 -timeout=10m ./... 及 with_low_memory 全套測試通過;pool/DNS 兩模式 race、go vet ./...、一般/低記憶體建置通過。首次 3 分鐘 timeout 失敗保留;重跑 listener/inbound 花 209.051 秒,未修改/略過測試。
  • 早期探針遇到 loopback source-port 衝突,以及 DNS AAAA 被全域 IPv6 開關攔截;修正 harness 後重跑受影響情境。失敗探針不混進最終 56 個程序,保留在 pilot/。
  • 下載本輪 evidence、harness、benchstat 與重跑說明。不含執行檔。核心未改動,文件提交不改寫實測核心 hash。
  • 未測 Linux TLS 代理/WAN、OpenWrt、UDP queue 壓力、低記憶體模式效能或 allocator burst 後回收上限的整程序 A/B;這不是後續優化規劃的全部驗收。

2026-09-05 程序記憶體與核心成本 A/B ​

本輪基線固定為 4a59a634,不重算上一輪已落地的改善。24 條 Paseo-managed Pi Grok 4.6 XHigh 診斷中,21 條經 Astra 審查後整合;沒有安全收益的 routing、缺乏證據的全域 pool classes、winner/TFO 生命週期尚未確認的 dial cancellation 不採用。Fast 指令交付已確認,但沒有獨立的服務端 priority 證據。

完整程序:不是 B/op,也不是 Linux RSS ​

最終比較 4a59a634 → 399e7f83。Windows 11、Ryzen 9 5900X、64 GB DDR4、Go 1.26.3、GOAMD64=v1、CGO_ENABLED=0、-trimpath -ldflags='-s -w'。執行時 GOMAXPROCS=2,不設定 GOGC/GOMEMLIMIT,不強制 GC。代理工作與編譯結束後串行執行;六個情境各七輪、輪替前後先後,共 84 個新程序。同情境所有輪次的 YAML SHA256 相同。

最小設定為 Rule 模式加 MATCH,DIRECT,DNS/TUN 關閉,loopback controller 僅供 readiness/規則數/釋放檢查;TCP 情境另開 loopback SOCKS。每個情境先等待 15 秒,再取約 250 ms 間隔的三秒樣本;表格是「每程序階段中位數」的七輪中位數,不把每個取樣點冒充獨立實驗。

下表單位均為 MiB;working set 括號內是七個程序的完整範圍。Private bytes 是 Windows private committed memory,與 working set 不同。

情境Before working set(範圍)After working set(範圍)Private bytes:前 → 後
空載19.19(18.97–19.29)19.17(19.07–19.27)50.32 → 50.43
100k 不連續 CIDR /3226.26(26.04–29.70)25.77(25.47–29.66)56.31 → 55.71
100k synthetic MRS domains30.63(30.48–30.74)30.65(30.55–30.83)60.94 → 60.96
100k synthetic GeoSite domains92.38(57.52–93.68)70.30(45.58–76.59)122.50 → 100.18
持有 100 條 TCP29.71(29.50–29.86)29.73(29.01–29.89)60.11 → 60.18
100 條 TCP 釋放後 60 秒30.55(30.46–30.63)30.58(30.25–30.71)61.00 → 61.17
持有 1,000 條 TCP113.43(110.61–115.91)115.48(113.11–116.14)144.85 → 147.43
1,000 條 TCP 釋放後 60 秒122.22(121.41–122.99)122.69(121.96–122.95)157.06 → 157.17
  • GeoSite working set 中位數 −23.9%,private bytes −18.2%。兩版範圍仍重疊,這不是每次啟動都保證少固定 RAM。
  • TCP workload 為獨立 loopback echo 程序,每條連線驗證 SOCKS 握手與 echo,再上/下傳各 128 × 4096 bytes,最多 16 個 worker。1,000 條連線每方向共 500 MiB。全部確認關閉,controller 連線數歸零。1,000 條連線流量期間 working set 為 117.02 → 117.34 MiB,流量完成仍持有時為 122.25 → 122.71 MiB;釋放後 15 秒為 122.22 → 122.69 MiB。
  • 沒有證明 TCP 程序 RAM 降低或在 60 秒內回到空載;持有 1,000 條連線的中位數反而 +1.8%。回收物件、歸還 pool、降低 B/op,都不等於立即歸還 OS 記憶體。
  • CIDR 短期 working set 不穩定:先前候選 4a5d6937 的獨立七輪是 26.27 → 29.44 MiB(+12.1%),最終候選是 −1.9%。後續程式只修正未在此情境執行的 binary export 與 ShadowQUIC lint,不能把差異歸因為修好了程序 RAM。另行、明確強制 GC 的 heap 診斷確認約 4.58 MiB 重複 IPRange 儲存不再存活;它只驗證引用解除,不取代自然程序結果。

相同來源的 CPU/alloc A/B ​

主矩陣比較 4a59a634 → 4a5d6937:14 個 package、38 個 case、七輪,共 532 筆。每個 case/樣本使用新程序、完全相同的 benchmark-only source、GOMAXPROCS=1、-test.cpu=1、每項目標至少兩秒。CIDR export 回退經 ca548d9f 修正後,另以最終 399e7f83 重跑四個 CIDR case、56 筆;其他 34 個 case 未重測成最終 hash,原結果仍標記 4a5d6937。

工作Before → After配置 bytes/allocs:前 → 後
DNS IPv4 cache hit675.8 → 178.7 ns(−73.6%)512/11 → 88/3
Deadline refresh171.70 → 61.61 ns(−64.1%)128/2 → 0/0
TCP tracker:流控啟用2468.0 → 850.5 ns(−65.5%)7680/18 → 688/9
Traffic-control session:global1372.0 → 122.5 ns(−91.1%)7200/11 → 208/2
UDP 單一 destination 建立931.7 → 547.6 ns(−41.2%)3796/11 → 2788/7
UDP destination lookup24.89 → 16.99 ns(−31.7%)0/0 → 0/0
SOCKS UDP handleSocksUDP286.6 → 221.5 ns(−22.7%)596/6 → 208/3
Domain suffix constructor64.25 → 32.40 ns(−49.6%)64/2 → 32/1
Kernel-direct address eviction76.81 → 60.93 µs(−20.7%)47526/27 → 47156/25
CIDR export 10k(最終候選重跑)230.28 → 53.92 µs(−76.6%)655488/6 → 655488/6

上述時間改善的 Mann–Whitney U p=0.001,SOCKS UDP 為 p=0.011;未作多重比較校正。完整範圍與每筆 raw sample 在證據包中。這是開發機,不是固定頻率、隔離負載的伺服器。

保留的代價與沒有宣稱的改善 ​

  • 預設 TCP tracker:352 → 256 B/op、仍 3 allocs;時間 308.5 → 295.5 ns(p=0.620)。UnwrapReader 則 3.337 → 4.701 ns,+40.9%(p=0.001),仍零配置。
  • UDP 第二個 destination 中位數 +20.2%(p=0.259,未達顯著);第二個及以上映射多 144 B。100/1,000 mapping timing 持平,不能只展示單一 destination 的收益。
  • VLESS 0-RTT:17616 → 16434 B/op,但 27 → 29 allocs;89.59 → 89.85 µs(p=1.000)。不宣稱握手加速。
  • NAT hit +2.4%(p=0.026);DoT 中位數 +5.0%(p=0.383);ReadCached 中位數 +2.1%(p=1.000),只少一次配置。TCP relay、sing handlePacket、xHTTP sequential 未有顯著加速。
  • 原 CIDR export 是 226.6 → 387.0 µs(+70.8%);原始回退保留,修正後直接編碼 compact 表,獨立 wire reference/mixed/mapped/邊界測試通過。
  • GeoSite 同清單不同新 attribute variants 會重新解碼 raw list;同 matcher cache 仍保留。確定性 fake-loader 測試中,一對順序 variants 由一次變成兩次載入;本輪未量完整大清單的順序變體 CPU A/B,這是載入 CPU 與留存記憶體的取捨。
  • 不降低 queue/cache 上限、TTL 或功能;不使用 GC 調參。Kernel-DIRECT TC eBPF 仍停用,真實 TUN/OpenWrt RAM 與 WAN 未重測。sync.Pool 也不是永久 leak 修復或由 GOMAXPROCS 保證的容量上限。

證據與驗證 ​

下載本輪原始資料、相同來源 harness 與重跑說明。包含兩個程序 cohort、兩個 microbench revision、修正前回退結果、fixtures 產生器、binary/config/harness hashes;不含 executable 或私人代理對話。

最終候選的全模組 go test -p=1 -count=1 ./... 通過;變更範圍 race、vet、VMess with_low_memory、gofmt 通過;golangci-lint 2.12.2 在 CGO 開/關皆為 0 issues。實際驗證工具鏈是 Go 1.26.3,不冒稱在 Go 1.20 執行測試;遠端 CI 與部署不在這些本地結果的證明範圍內。

2026-08-28 核心 hot-path 優化 A/B ​

這輪比較 Aster 90f0e4ee(本輪優化前)與 72048c8a(效能 commit 9824ccdb 加 lint 修正)。兩版各自建立獨立 Windows amd64 test binary;每個樣本都是新程序,固定 GOMAXPROCS=1、-test.cpu=1、GOAMD64=v1、Go 1.26.3、每項 2 秒。Aster 前/後交錯並輪替先後順序,共 7 輪。

主矩陣涵蓋 11 個 package、每版 24 個同名 case、每版共 168 筆樣本。表格採 7 輪中位數,括號內是完整範圍。下列改善的 benchstat Mann–Whitney U 檢定皆為 p=0.001。測試機是 Windows 11、Ryzen 9 5900X(12C/24T)、64 GB DDR4;測前總 CPU 取樣為 9.85–17.22%,測後為 7.19–15.71%。這仍是開發機 microbenchmark,不是 WAN 吞吐量。

Aster 核心工作優化前中位數(範圍)優化後中位數(範圍)結果配置量
Kernel DIRECT 既有 flow refresh221.0 ns(208.2–243.9)182.0 ns(179.8–185.9)1.21×;時間少 17.6%0 B/0 → 0 B/0
NAT 既有 flow lookup64.26 ns(63.99–65.38)11.67 ns(11.58–12.09)5.51×;時間少 81.8%0 B/0 → 0 B/0
UDP WriteBack target 更新7.515 ns(7.491–7.802)3.918 ns(3.908–4.084)1.92×;時間少 47.9%0 B/0 → 0 B/0
DomainSet.Has(short)166.7 ns(165.0–170.5)56.32 ns(54.96–56.96)2.96×;時間少 66.2%0 B/0 → 0 B/0
合併後 100k CIDR miss84.63 ns(83.60–90.87)12.93 ns(12.57–15.84)6.55×;時間少 84.7%0 B/0 → 0 B/0
GetUser(10,000 users)56.67 ns(55.23–61.04)40.26 ns(37.26–47.98)1.41×;時間少 29.0%0 B/0 → 0 B/0
上傳流量累加3.561 ns(3.533–3.652)1.742 ns(1.717–1.852)2.04×;時間少 51.1%0 B/0 → 0 B/0
TCP tracker lifecycle574.7 ns(561.3–592.6)257.6 ns(253.5–314.1)2.23×;時間少 55.2%528 B/8 → 352 B/3
預設規則 match22.97 ns(22.78–23.30)20.13 ns(20.03–20.53)1.14×;時間少 12.4%0 B/0 → 0 B/0
UDP handlePacket(同一 benchmark-only harness)299.8 ns(259.1–353.4)187.4 ns(170.5–230.0)1.60×;時間少 37.5%280 B/8 → 208 B/2

控制組與沒有宣稱的結果 ​

  • UDP metadata pool 的生產程式在兩版完全相同。一般 test binary 一度顯示 11.50 → 13.18 ns;換成兩版完全相同的最小 harness 後是 12.84 → 12.73 ns(p=0.874),確認是 test layout 差異,不是 pool 回退。完整 handlePacket 則確實減少 37.5% 時間與 75% 配置次數。
  • 100/1,000 個 UDP mapping insert 沒有顯著改變(1,000 個:348.2 → 331.1 µs,p=0.097);配置量維持 615,856 B/2,049 allocs。
  • AnyTLS WriteDataFrame 的 1 KiB/16 KiB/64 KiB case 都維持 0 配置;1 KiB 與 16 KiB 中位數持平,64 KiB 的變化未達顯著(p=0.073)。
  • 共用 32 KiB relay 中位數 3.766 → 3.959 µs(+5.1%),tunnel TCP relay 3.899 → 4.023 µs(+3.2%);兩組前後範圍都重疊。這輪不宣稱桌面 TCP 加速,也不拿這組數字改寫下方 OpenWrt 實機結果。若要判定 3–5% 是否為真實回退,必須在固定 CPU/OpenWrt 實機再跑。

這組 A/B 的用途是驗證 9824ccdb 的 Aster 內部 hot path。它沒有使用 Mihomo binary,也沒有測外部節點;因此只能回答「這個 Aster commit 是否降低核心成本」,不能回答「下載速度會增加多少」。

2026-08-24 review wave 本機驗證 ​

這一輪以 8462a265 為基底的未發布工作樹,對 Mihomo v1.19.30(ac017cdd)重新建立獨立 Windows amd64 test binary。每個樣本都是新程序,固定 GOMAXPROCS=1、-test.cpu=1、GOAMD64=v1、Go 1.26.3、每輪 2 秒;Aster/Mihomo 交錯 7 輪。這是 5900X 開發機的 regression validation,不取代下方 OpenWrt 主要結果,也不是 WAN 吞吐量。

核心工作Mihomo 1.19.30 中位數(範圍)Aster 中位數(範圍)配置量
UDP metadata54.44 ns(49.80–71.77)12.22 ns(11.76–12.48),4.45×416 B/1 → 0 B/0
停用 debug log206.3 ns(204.3–245.5)2.132 ns(2.096–3.063),96.8×24 B/1 → 0 B/0
AnyTLS frame 1 KiB72.55 ns(66.80–82.65)31.67 ns(30.93–33.76),2.29×64 B/1 → 0 B/0
AnyTLS frame 16 KiB322.8 ns(320.4–354.0)244.3 ns(218.4–277.3),1.32×64 B/1 → 0 B/0
共用 relay 32 KiB4.038 µs(4.005–4.157)3.916 µs(3.763–4.477)64 B/1 → 0 B/0
tunnel TCP relay 32 KiB4.079 µs(3.892–4.666)4.016 µs(3.840–4.198)64 B/1 → 0 B/0

兩個 TCP relay 的範圍重疊,所以這輪只確認 Aster 維持零配置,沒有宣稱桌面 TCP 顯著加速。UDP、log 與 AnyTLS 的配置量和方向則與 OpenWrt 結果一致。

同一工作樹內、同一台機器的 review 前後回歸 benchmark 顯示:

Aster-only hot pathReview 前Review 後結果
Kernel DIRECT 既有 flow refresh15.286 µs;64 B/1 alloc270.8 ns;0 B/0 alloc56.4×;未到期時不再全表掃描
合併後 100k 個不連續 CIDR 的 miss2.385 ms;0 alloc114.7 ns;0 alloc約 20,790×;恢復二分搜尋
UDP association 新增 1,000 個 mapping41.31 ms;46.37 MB486.3 µs;542.6 KiB84.9×;移除每次完整 map copy

Windows 完整核心、相同最小設定、啟動後 15 秒的五輪中位數:Aster working set 19.00 MiB、Mihomo 18.07 MiB(Aster +0.93 MiB/+5.2%);private bytes 52.52 MiB 對 51.29 MiB(+1.23 MiB/+2.4%)。這些是 Windows 指標,不能與 Linux RSS/PSS 混用;也再次證明不能宣稱 Aster 空載一定比較省 RAM。

OpenWrt 實機比較(主要結果) ​

比較基線為 Mihomo v1.19.30、commit ac017cdd246ce8bd547653d927e7bf77d7ee73d5。Aster 為 0590d3a4 當時的 main(含這波 review/修正與 128 KiB frame pool)。兩個版本各自以相同 Go 版本、target、flags 與 benchmark harness 建立 go test -c Linux amd64 binary,依序執行三輪,每輪至少 2 秒;表格採三輪中位數。2026-08-19 在同一台 OpenWrt 軟路由、於修正落地後重跑。

環境項目實際值
系統OpenWrt,Linux 6.6.86,x86-64
CPURyzen 7 5825U 宿主,VM 配置 12 vCPU
CPU 頻率測試前客體取樣平均 2.342 GHz。這是 /proc/cpuinfo 頻率取樣,Hypervisor 未保證恆定
記憶體VM 配置約 6 GB(MemTotal 6081752 kB)
DRAM 頻率Hypervisor 未提供,無法取得
Go1.26.3,GOAMD64=v1 交叉編譯的 Linux amd64 test binary
測試前 load average0.07/0.03/0.00(1/5/15 分鐘)
測試後 load average0.92/0.37/0.14(1/5/15 分鐘)
核心工作Mihomo 1.19.30 中位數Aster 中位數Aster 相對結果
UDP packet metadata70.00 ns;416 B/1 alloc12.89 ns;0 B/0 alloc5.43× faster;消除 416 B 配置
停用的 debug log228.0 ns;24 B/1 alloc2.254 ns;0 B/0 alloc101× faster;消除 event 配置
AnyTLS frame(1 KiB)71.54 ns;64 B/1 alloc35.98 ns;0 B/0 alloc1.99× faster;延遲降低 50%
AnyTLS frame(16 KiB)267.1 ns;64 B/1 alloc196.8 ns;0 B/0 alloc1.36× faster;延遲降低 26%
AnyTLS frame(64 KiB)無同名 bench1.148 µs;0 B/0 alloc獨立 WriteDataFrame/65536 fixture 在 128 KiB pool 後為零配置
TCP relay(32 KiB)4.546 µs;7.21 GB/s;64 B/1 alloc4.449 µs;7.37 GB/s;0 B/0 alloc延遲降低 2.1%;吞吐提高 2.2%

三輪測量範圍 ​

BenchmarkMihomo 1.19.30 範圍Aster 範圍
UDP packet metadata69.74–72.12 ns/op12.84–13.00 ns/op
停用的 debug log223.6–231.5 ns/op2.230–2.270 ns/op
AnyTLS frame(1 KiB)71.35–71.69 ns/op35.76–36.89 ns/op
AnyTLS frame(16 KiB)264.9–271.8 ns/op195.2–216.4 ns/op
TCP relay(32 KiB)4.538–4.576 µs/op4.423–4.505 µs/op

這台 5825U 軟路由仍比許多 MT7621、低階 ARM 或廉價 VPS 強。這組結果只能證明優化在實際 OpenWrt 環境仍有效;越弱的裝置,絕對 GB/s 一定更低,改善比例也必須在該裝置重新測量。

Hyper-V 低階資源模擬(次要結果) ​

為了更接近大部分使用者的弱硬體,我們在同一台 Hyper-V OpenWrt VM 裡再加一層資源限制。Benchmark 只准使用 1 顆 vCPU,而且每 100 ms 最多只能執行 25 ms,相當於 單核 25% CPU 時間;記憶體上限設為 512 MiB,並停用 swap。2026-08-19 用 Mihomo v1.19.30 與同一組 Linux amd64 test binary 重跑。

這是資源不足情境的模擬,不是 MT7621 或 ARM 指令集模擬。它能回答「CPU 很慢、記憶體很少時,Aster 的優化是否仍存在」,但不能取代真實低階路由器測試。

環境項目實際值
系統Hyper-V 上的 OpenWrt,Linux 6.6.86,x86-64
宿主 CPURyzen 7 5825U
CPU 限制綁定 vCPU 0;cgroup cpu.max = 25000 100000,即單核 25% CPU 時間
客體回報 CPU 頻率未限速段開始前平均 2.342 GHz。這是頻率取樣,不包含 25% CPU 時間限制
記憶體限制512 MiB、無 swap
DRAM 頻率Hypervisor 未提供,無法取得
Go1.26.3,Linux amd64 test binary
測試前 load average0.07/0.03/0.00(與未限速段同一輪開始前)
測試後 load average0.31/0.31/0.14(1/5/15 分鐘)
節流確認3,709 個 CPU quota periods 中有 3,660 個發生 throttling

兩個版本依序跑相同 benchmark,各三輪、每輪至少 2 秒。以下採三輪中位數:

核心工作Mihomo 1.19.30 中位數Aster 中位數Aster 相對結果
UDP packet metadata251.5 ns;416 B/1 alloc48.01 ns;0 B/0 alloc5.24× faster;消除 416 B 配置
停用的 debug log854.3 ns;24 B/1 alloc8.054 ns;0 B/0 alloc約 106× faster;消除 event 配置
AnyTLS frame(1 KiB)331.0 ns;64 B/1 alloc139.6 ns;0 B/0 alloc2.37× faster;延遲降低 58%
AnyTLS frame(16 KiB)1.080 µs;64 B/1 alloc790.8 ns;0 B/0 alloc1.37× faster;延遲降低 27%
TCP relay(32 KiB)17.644 µs;1.86 GB/s;64 B/1 alloc16.812 µs;1.95 GB/s;0 B/0 alloc延遲降低 4.7%;吞吐提高 5.0%

峰值記憶體佔用 ​

先把兩種「記憶體佔用」分開看:

情境Mihomo 1.19.30Aster結論
完整核心、最小設定、空載 15 秒的 RSS34.7 MiB39.3 MiBAster 多 4.6 MiB,約 +13%

完整核心測試使用相同設定:Direct 模式、無代理、無規則、靜默 log、IPv6 關閉、TUN 關閉、只綁 127.0.0.1 未使用埠;Aster 與 Mihomo 各三輪,每輪啟動後等待 15 秒。RSS 三輪範圍為 Aster 38.5–39.3 MiB、Mihomo 34.7–35.0 MiB。OpenWrt 核心沒有提供 smaps_rollup,因此沒有 PSS。只報 /proc/<pid>/status 的 VmRSS。

所以不能說 Aster 空載比較省 RAM。 Aster 加入更多功能與程式碼,最小設定的常駐底座目前比 Mihomo 1.19.30 多約 4.6 MiB。下面的獨立程序峰值只代表「重複執行某個 hot path 的 Go 測試程式」當時的 Max RSS,不是完整代理掛著規則、GeoIP、DNS cache 和連線時的總 RAM。

核心 hot path 的獨立程序峰值 ​

每個案例改用獨立程序重跑三輪,透過 Linux /usr/bin/time -v 記錄 Max RSS。表格採三輪中位數;兩邊都套用相同的單核 25% CPU 時間與 512 MiB 記憶體限制。

核心工作Mihomo 1.19.30 峰值 RSSAster 峰值 RSS差異
TCP relay(32 KiB)40.5 MiB51.8 MiBAster 多 28%
停用的 debug log33.5 MiB23.0 MiB少 31%
UDP packet metadata53.0 MiB53.4 MiB持平
AnyTLS frame(1 KiB)35.4 MiB26.0 MiB少 27%
AnyTLS frame(16 KiB)34.8 MiB25.5 MiB少 27%

對 1.19.30 重跑後,不能再寫「五個案例都少 30–49%」。停用 log 與 AnyTLS 仍明顯較低;UDP 持平;TCP 這個獨立測試程序的峰值反而較高。這比較的是該 package 測試程式的暫存壓力,不能外推成「Aster 在任何設定下整體 RAM 都比較小」。Aster 測試程式比 Mihomo 大(含更多套件),RSS 比較的是整個 Go test binary,不是單一 frame。

三輪範圍分別為:TCP Mihomo 39.5–40.5 MiB、Aster 51.5–52.0 MiB;log Mihomo 32.5–33.5 MiB、Aster 固定 23.0 MiB;UDP Mihomo 50.8–53.9 MiB、Aster 53.0–54.9 MiB;AnyTLS 1 KiB Mihomo 35.0–35.5 MiB、Aster 固定 26.0 MiB;AnyTLS 16 KiB Mihomo 34.7–35.1 MiB、Aster 25.5–26.0 MiB。所有測試皆為 0 swap。

受限環境三輪範圍 ​

BenchmarkMihomo 1.19.30 範圍Aster 範圍
UDP packet metadata244.3–256.4 ns/op47.85–48.21 ns/op
停用的 debug log854.1–856.9 ns/op7.984–8.157 ns/op
AnyTLS frame(1 KiB)323.4–335.5 ns/op135.5–155.2 ns/op
AnyTLS frame(16 KiB)1.075–1.092 µs/op772.3–801.6 ns/op
TCP relay(32 KiB)17.598–17.756 µs/op16.771–16.841 µs/op

受限後的絕對處理時間約變成原本的四倍,證明 CPU quota 確實生效;Aster 在五項延遲工作中仍全部較快。在這個相同 x86 VM 的 CPU quota 實驗裡,TCP 改善由未限速的 2.1% 增至 4.7%,AnyTLS 1 KiB 由 1.99 倍增至 2.37 倍。UDP 倍率則由 5.43 倍略降到 5.24 倍;這不能外推成「所有較弱硬體都會有更大提升」,ARM/MIPS、cache 與記憶體頻寬仍須實機重測。首頁仍採用未限速 OpenWrt 對 Mihomo 1.19.30 的 TCP 約 2%,不使用這組較好看的 4.7% 當主要宣傳數字。

各協議 loopback 測試 ​

核心 microbenchmark 變快,不等於每一種協議的端到端吞吐都會同步增加。為了驗證這點,我們先前在 Linux Docker host network 上,讓 Aster 與 Mihomo 連到相同的本機服務端,以 16 KiB chunk 分別測量讀寫。下表仍是對 Mihomo v1.19.29 的結果;這次沒有用 v1.19.30 重跑協議服務端。

協議服務端Aster 與 Mihomo 的結果
Direct(控制組)無讀寫差距在約 3% 內,視為持平
Shadowsocks AES-256-GCMshadowsocks-rust分配量相同;寫入樣本波動過大,不下加速結論
VMessV2Fly 4.45.2寫入約 645 vs 647 MB/s;讀取約 488 vs 488 MB/s,持平
VLESS + TLSV2Fly 4.45.2交錯三輪的典型差距約 -2% 至 +3%,持平
Trojan + TLStrojan 1.16交錯五輪中位數:寫入 335 vs 341 MB/s、讀取 313 vs 314 MB/s,持平
Snell v3 + HTTP obfsOpenSnell 3.0.1交錯三輪中位數:寫入 429 vs 430 MB/s、讀取 763 vs 774 MB/s,持平
Hysteria v1Hysteria 1.3.5,100 Mbps兩邊皆約 12.25–12.28 MB/s,受服務端頻寬上限限制
AnyTLS目前沒有同條件 loopback 服務端不混用外部節點;只列前面的 frame microbenchmark

表內數字順序皆為 Aster vs Mihomo。VMess、VLESS、Trojan、Snell、Shadowsocks 的 B/op 與 allocs/op 也大致相同,表示這次優化主要改善共用 relay、UDP metadata、log 與 AnyTLS frame 等底層 hot path,沒有神奇地讓所有加密協議一起大幅變快。

這組協議測試仍是先前對 Mihomo v1.19.29 的 Docker loopback,沒有用 v1.19.30 重跑。這次 5900X 主機有 Docker Engine,但沒有當時釘死的協議服務端映像,補拉映像再比一次端到端吞吐,無法對上原 SHA。這些協議的加密與搬運路徑兩邊共用,1.19.30 的 microbenchmark 已覆蓋有改動的 relay/UDP/log/AnyTLS frame;協議表只用來說明「沒有讓所有加密協議一起大幅變快」,不作為首頁宣傳數字。

服務端映像固定為 shadowsocks-rust sha256:85d01d…e1359、V2Fly sha256:e81a07…de78c、Trojan sha256:5b36c2…b98b5f7、OpenSnell sha256:70053f…345467、Hysteria v1.3.5 sha256:4c8c92…f1e35。

UDP metadata 配置前後 ​

同一支 benchmark 同時量測 Mihomo 的直接建立 metadata 路徑與 Aster 的物件池路徑,因此可以做同機比較:

路徑時間記憶體配置
Mihomo 1.19.30 每個封包直接建立69.74–72.12 ns/op416 B/op、1 alloc/op
Aster metadata pool12.84–13.00 ns/op0 B/op、0 allocs/op

物件池路徑以三輪中位數計算約快 5.43 倍,並消除每封包 416 bytes 的 heap allocation。

高階開發機結果(附錄) ​

最初的 Aster 絕對數據是在高階桌機上取得,只適合做開發期回歸檢查,不應拿來代表一般路由器。2026-08-19 在同一台 5900X 上,對 Mihomo v1.19.30 與 Aster main 交錯重跑同一組 microbenchmark(各三輪、每輪至少 2 秒):

環境項目實際值
系統Windows 11 amd64;平衡電源模式
CPURyzen 9 5900X,12 cores/24 logical CPUs
回報頻率Windows \Processor Frequency 計數器固定 3701 MHz(標稱時脈,不是實際 boost)
DRAM64 GB(4×16 GB)G.Skill DDR4-3600,configured 3600 MT/s
Go1.26.3 windows/amd64
測試前 CPU load各項開始前取樣平均約 30%(17.6–60.9%)
測試後 CPU load各項結束後取樣平均約 34%(20.3–54.8%)
processor queue全程 0

背景負載仍然不低,所以 5900X 不是首頁的主要比較;它只用來確認桌面開發機沒有明顯回退。同一天稍早一輪因背景約 42–47% 且未交錯,TCP 數字互相打架,已丟棄。修正落地後的交錯重跑:

核心工作Mihomo 1.19.30 中位數Aster 中位數Aster 相對結果
UDP packet metadata152.4 ns;416 B/1 alloc11.91 ns;0 B/0 alloc12.8× faster;消除 416 B 配置
停用的 debug log455.3 ns;24 B/1 alloc2.268 ns;0 B/0 alloc201× faster;消除 event 配置
AnyTLS frame(1 KiB)74.99 ns;64 B/1 alloc34.70 ns;0 B/0 alloc2.16× faster
AnyTLS frame(16 KiB)260.0 ns;64 B/1 alloc184.0 ns;0 B/0 alloc1.41× faster
AnyTLS frame(64 KiB)無同名 bench1.056 µs;0 B/0 alloc獨立 WriteDataFrame/65536 fixture 為零配置
TCP relay(32 KiB)10.044 µs;3.26 GB/s;64 B/1 alloc11.436 µs;2.87 GB/s;0 B/0 alloc範圍重疊;此輪 Aster 中位數較慢,不以 5900X TCP 下加速結論

三輪範圍:UDP Mihomo 145.8–155.1 ns、Aster pool 11.69–12.21 ns;log Mihomo 442.9–456.5 ns、Aster 2.227–2.352 ns;AnyTLS 1 KiB Mihomo 74.12–77.73 ns、Aster 33.21–36.36 ns;AnyTLS 16 KiB Mihomo 257.9–271.6 ns、Aster 182.9–200.4 ns;TCP Mihomo 9.963–11.705 µs、Aster 10.394–11.947 µs。32 KiB Relay32KiBComparison 中位數 Aster 9.332 µs 對 Mihomo 10.775 µs。桌面 TCP 在背景負載下波動大,首頁仍用 OpenWrt。

如何重跑 ​

本輪 Aster hot-path suite 可從 repository 根目錄執行:

sh
GOAMD64=v1 GOMAXPROCS=1 go test \
  ./component/kerneldirect ./component/nat ./component/trie ./component/cidr \
  ./component/aster ./listener/sing ./tunnel ./tunnel/statistic \
  -run '^$' \
  -bench 'Benchmark(ObserveFlowRefresh|WriteBackProxyUpdate|TableExistingFlow|DomainSetHas|IpCidrSetMergedMiss|ManagerGetUser|ManagerPushUploaded|TCPTrackerLifecycle|MatchDefaultRule|PacketMetadata)$' \
  -benchmem \
  -benchtime=2s \
  -count=7 \
  -cpu=1

要做 commit 前後 A/B,請從兩個 commit 分別用 go test -c 建立 test binary,每個樣本以新程序啟動,並輪替 before/after 的先後順序。不要在同一個仍會重新編譯的 go test 程序中混入編譯時間,也不要只比較不同機器的單次結果。

舊有 Aster/Mihomo suite:

sh
go test \
  ./common/net ./component/nat ./constant ./listener/sing ./log \
  ./transport/anytls/padding ./transport/anytls/session \
  ./tunnel ./tunnel/statistic \
  -run '^$' \
  -bench 'Benchmark' \
  -benchmem \
  -count=3

對照 Mihomo 時請使用 tag v1.19.30(ac017cdd)建置相同的 Linux amd64 test binary,在同一台機器依序跑三輪。不要用不同機器的單次結果推算百分比。

數據如何解讀 ​

  • 這些是 in-process microbenchmarks,主要量測 Aster Core 自身的額外開銷。
  • TCP 與 AnyTLS 的 GB/s 是記憶體/net.Pipe 路徑,不是 WAN 實際吞吐量。
  • 真實代理速度還會受到加密、RTT、丟包、MTU、NIC、作業系統、CPU 架構與服務端影響。
  • 獨立 WriteDataFrame fixture 的 1 KiB、16 KiB 與 64 KiB case 為零配置(64 KiB 靠 128 KiB 物件池);這不代表完整 AnyTLS session 的所有路徑都零配置。更大或不對齊的 buffer 仍可能配置。
  • 微基準最適合防止效能回退,而不是承諾任何裝置都會得到相同網速。

TC eBPF 的實機反例 ​

Kernel DIRECT 本身不要求 TC eBPF。推薦的 OpenWrt 路徑是由 nftables learned exclude set 把 DIRECT 交回 Linux forwarding/NAT,並保留 flow offload。實驗性 kernel-direct-ebpf 則會在 LAN ingress 對每個封包查 generation、IPv4/IPv6 LPM、40-byte 5-tuple LRU 與 per-CPU counters。

在同一台路由器、同一 Speedtest server ID 37639 的 A/B 中:

狀態Download
TC eBPF 開啟692,335,768 bps
暫時卸載 TC filters1,647,299,448 bps
持久關閉 TC、重啟後1,643,651,288 bps

卸載後約為開啟時的 2.37 倍,也回到該網路原本約 1.7 Gbps 的級別。主要原因是這個 TC ingress hook 妨礙 OpenWrt flow offload,加上 classifier 本身逐封包執行;它不代表其他 eBPF 程式或其他硬體一定較慢。

這也是 microbenchmark 與實際網路結果必須分開看的例子:某個 map lookup 或資料結構很快,不代表把它放進每個 ingress packet、並改變 kernel offload 路徑後,端到端吞吐量就會更高。啟用 TC 前後應固定 client、server ID、協定與時間區間各跑多次;只要沒有明確收益,就維持 kernel-direct-ebpf: false。