效能基準測試
效能數字必須連同量測條件一起解讀。
本頁記錄 2026 年 10 月 11 日至 12 日,在 Apple M2 主機上的 OrbStack 容器環境中進行的效能比較。受測軟體包括 Pingclair、nginx、Caddy 與 Traefik,測試涵蓋反向代理、靜態檔案傳輸、動態 Zstd 壓縮、存取日誌開銷,以及 HTTP/3。
所有結果均來自特定硬體、軟體版本與工作負載下的受控量測。數據反映的是本次實驗條件下的相對表現,不應直接視為其他部署環境的效能預測或容量保證。
🧭 量測方法
Section titled “🧭 量測方法”本節記錄產生各組數據所使用的硬體、虛擬機、容器配置、軟體版本與測試命令。
四組測試共用相同的虛擬機與容器網路,並使用同一組受測軟體映像,以減少環境差異對結果的影響。
🖥️ 主機與虛擬機
Section titled “🖥️ 主機與虛擬機”- 實體主機: MacBook Pro(2022 年),搭載八核心 Apple M2、16 GB 統一記憶體,執行 macOS 27.0.1。
- 虛擬機: OrbStack 2.2.3,Linux 核心版本為
7.0.14-orbstack,配置 8 vCPU 與 8 GB 記憶體。受測伺服器、負載產生器及後端服務均執行於同一台 Linux VM 內;逐項測試由 VM 內的控制容器負責調度。 - 容器引擎: Docker 29.4.0(OrbStack engine)。所有容器均連接至專用 bridge 網路
bench-net(192.168.97.0/24),用戶端與伺服器位於同一子網,測試流量不經過外部網路。
📦 受測版本與映像
Section titled “📦 受測版本與映像”| 元件 | 版本 | 映像與建置方式 |
|---|---|---|
Pingclair main |
main@0476be4a(0.0.0-dev) |
bench/pingclair-main:0476be4a;使用 Rust 1.99.0 執行 cargo build --release --locked |
Pingclair v0.2.2 |
0.2.2 |
bench/pingclair:0.2.2;採用官方 pingclair-linux-aarch64 發行產物,並已依 SHA256SUMS 驗證 |
| nginx | 1.31.6 |
bench/nginx:1.31.6;由官方原始碼建置,使用 OpenSSL 3.5.5、GCC 15.2,並加入 Zstd 模組 |
| Caddy | 2.11.7 |
官方容器映像 caddy:2.11.7 |
| Traefik | v3.7.14 |
官方容器映像 traefik:v3.7.14 |
nginx 建置時啟用了以下模組與參數:
--with-http_ssl_module--with-http_v2_module--with-http_v3_module--with-http_realip_module--with-http_stub_status_module--add-module=zstd-nginx-module@057a7d3其中 Zstd 使用第三方模組 tokers/zstd-nginx-module,版本固定於提交 057a7d3。
⚙️ 容器與伺服器配置
Section titled “⚙️ 容器與伺服器配置”所有受測容器均使用 linux/arm64 架構,並透過 Docker 的 --cpus=2 限制 CPU 使用量。
容器內的設定檔、測試資料與 TLS 憑證均以唯讀方式掛載。日誌目錄及 nginx 快取目錄則使用各 512 MB 的 tmpfs,避免將持久化儲存裝置的寫入延遲引入測試。
容器啟動參數如下:
docker run -d --name sut --network bench-net --cpus=2 \ --tmpfs /logs:size=512m \ --tmpfs /var/cache/nginx:size=512m \ -v <bench>/configs:/conf:ro \ -v <bench>/payloads:/payloads:ro \ -v <bench>/certs:/certs:ro \ <image> <server command>各伺服器均保留自身預設的 worker 或執行緒設定,不強制統一其內部並行模型。例如,nginx 使用 worker_processes auto 與 worker_connections 1024。
TLS 與連接埠
測試使用自簽 TLS 憑證,憑證主體 CN 為 sut,SAN 包含 sut、localhost 與 127.0.0.1。
HTTPS 使用 8443/TCP,HTTP/3 測試另使用 8443/UDP。
靜態檔案
測試包含三種大小的檔案:
| 檔案 | 大小 |
|---|---|
index.html |
226 B |
small.bin |
10 KiB |
medium.bin |
2 MiB |
反向代理後端
後端採用 node:22-slim 容器,啟動四個 Node.js HTTP 服務實例,分別監聽 TCP 3001 至 3004。
各實例的監聽 backlog 設為 4096,並回傳固定的 16 B JSON 回應。
四套反向代理均採用 round-robin 分配請求,並配置上游持久連線重用。nginx 使用 keepalive 64,Traefik 使用 maxIdleConnsPerHost: 64。各實作的連線池語意並非完全相同,因此這些設定代表近似的配置目標,而非內部行為完全等價。
存取日誌
除日誌開銷測試外,所有吞吐量測試均停用存取日誌。
日誌開銷測試分為兩個階段:
- A:停用存取日誌。
- B:啟用 JSON 格式的存取日誌,並寫入 512 MB tmpfs。
nginx 使用 escape=json,Pingclair 使用 format json。
動態 Zstd 壓縮
各伺服器採用以下設定:
| 伺服器 | Zstd 設定 |
|---|---|
| Pingclair | encode zstd,搭配相同的回應類型匹配條件 |
| nginx | zstd_comp_level 3、zstd_min_length 256、zstd_types * |
| Caddy | encode zstd better,並匹配 application/octet-stream |
上述設定均啟用動態 Zstd 壓縮,但不同實作的壓縮策略、輸出大小及 CPU 開銷未必完全一致。因此,壓縮組比較的是各伺服器在指定設定下的整體服務吞吐量,而非同一壓縮演算法實作的純計算速度。
🏎️ 負載產生器與測試命令
Section titled “🏎️ 負載產生器與測試命令”HTTP/2
HTTP/2 測試使用 nghttp2 1.68.0 提供的 h2load,執行於 client-lite 容器(Ubuntu 26.04,安裝 nghttp2-client)。
該容器與受測伺服器位於同一個 bridge 網路。
反向代理定點測試:
h2load -n 500000 -c 100 -t 16 -m 8 \ https://sut:8443/靜態檔案及日誌開銷測試:
h2load -n 500000 -c 100 -t 16 -m 8 \ https://sut:8443/index.html靜態檔案測試分別請求 index.html、small.bin 與 medium.bin。前兩者各發出 500,000 個請求,2 MiB 檔案則為 5,000 個請求。
Zstd 組額外加入以下請求標頭:
-H 'Accept-Encoding: zstd'高併發掃描由 benchmark.py 逐點執行:
h2load -n 20000 -c <c> -t min(c, 8) \ -m 8 -T 5 -N 5 <url>其中併發數 c 從 1 增加至 9,901,每次遞增 100。每個併發點發出 20,000 個請求。
HTTP/3
HTTP/3 測試使用 Tencent tquic 的 tquic_client,版本為 develop@938e90a,Cargo 套件版本 1.6.0,並使用 BoringSSL 建置。
測試命令:
tquic_client -t 8 \ --max-concurrent-conns 25 \ --max-concurrent-requests 25 \ --total-requests-per-thread 0 \ -d 10 <url>本組測試採用每條連線完成一個請求後即重新建立連線的模式,用以觀察 QUIC 連線頻繁建立與關閉時的整體處理能力。
測試調度
高併發掃描與多輪量測均由 Linux VM 內的 supervisor 容器執行。
該容器使用 Ubuntu 26.04 與 Docker CLI,透過掛載的 docker.sock 控制受測容器,負責逐項執行測試、處理重試與收集結果。macOS 宿主僅負責最終結果彙整,不直接參與逐項測試的調度。
📏 取樣與結果處理
Section titled “📏 取樣與結果處理”各測試組採用不同的請求數量與重複次數:
| 測試項目 | 量測方式 |
|---|---|
| 反向代理定點 | 100 併發,500,000 個請求,單次量測 |
| 高併發掃描 | 每個併發點 20,000 個請求,單次量測 |
| 靜態檔案與 Zstd | 兩輪量測,取中位數 |
| 存取日誌開銷 | 兩輪量測,取中位數 |
| HTTP/3 | Pingclair、Caddy 各兩輪;nginx 四輪,取中位數 |
有效性判定
反向代理定點、靜態檔案、壓縮及日誌開銷測試,僅採納全部請求成功的量測結果。出現請求失敗的樣本不納入這些組別的吞吐量比較。
高併發掃描則保留失敗樣本作為診斷資料,並在結果旁明確標示錯誤率。這些數值不得直接視為全部請求成功時的有效吞吐量。
HTTP/3 組另行記錄連線層失敗。表中列出的吞吐量保留實際觀察到的連線失敗影響,不將其視為完全零錯誤的量測。
測試期間的修正
初次測試發現,nginx 預設的 keepalive_requests 1000 在高併發情境下造成連線提前關閉,影響結果。
因此,nginx 的相關設定統一調整為:
keepalive_requests 1000000;完成調整後,重新執行整批 nginx 測試。本頁不引用調整前的數據。
📊 測試結果
Section titled “📊 測試結果”🔀 反向代理
Section titled “🔀 反向代理”本組使用四個 Node.js 後端,以 round-robin 分配請求,並維持上游持久連線重用。
定點吞吐量
測試條件為 100 併發、總計 500,000 個請求。所有列出的結果均為零錯誤。
單位:req/s。
| 伺服器 | req/s |
|---|---|
| nginx 1.31.6 | 84,133 |
Pingclair main |
78,348 |
Pingclair v0.2.2 |
77,545 |
| Traefik v3.7.14 | 30,315 |
nginx 在此工作負載下取得最高吞吐量。
Pingclair main 達到 nginx 約 93.1% 的吞吐量,相較 v0.2.2 提升約 1.0%。與 Traefik 相比,Pingclair main 的吞吐量約為其 2.58 倍。
上述差距僅適用於本組後端、連線配置及 CPU 限制,不代表各伺服器在所有反向代理情境下的相對效能。
高併發掃描
以下為四個代表性併發點。數值單位為 req/s,括號內為請求錯誤率;未標示錯誤率者代表該次量測零錯誤。
| 併發數 | nginx | Pingclair main |
Pingclair v0.2.2 |
Traefik |
|---|---|---|---|---|
| 101 | 69,201 | 71,227 | 70,603 | 30,028 |
| 1,001 | 22,182 (1.8%) | 26,958 | 31,411 | 10,272 |
| 5,001 | 6,364 (61%) | 5,908 (40%) | 9,010 | 2,906 (12%) |
| 9,901 | 2,189 (62%) | 1,606 (69%) | 4,880 | 325 (85%) |
在約 100 併發時,nginx 與兩版 Pingclair 的掃描吞吐量均接近 70,000 req/s,Traefik 則約為 30,000 req/s。
隨著併發數提高,各伺服器的吞吐量均明顯下降,部分實作也開始出現大量連線或請求失敗。這些結果受到 2 CPU 容器配額、8 GB VM 容量,以及負載產生器本身的資源使用共同影響,不能單獨歸因於伺服器實作。
值得注意的是,Pingclair v0.2.2 是本次掃描中唯一在 9,901 併發下仍維持零錯誤的受測版本。
相較之下,main 在 5,001 與 9,901 併發時的錯誤率分別達到 40% 與 69%。新版在一般負載下的吞吐量略有提升,但極端高併發情境下的穩定性明顯不如 v0.2.2。
2026-10-12 複查
針對上述差異,重新執行 main 與 v0.2.2 在 5,001 及 9,901 併發下的測試,每列仍為 20,000 個請求。
複查結果如下:
| 併發數 | main 錯誤率 |
v0.2.2 錯誤率 |
|---|---|---|
| 5,001 | 44% | 0% |
| 9,901 | 59% | 0% |
main 的失敗均發生於連線層,未觀察到 HTTP 5xx 回應。
v0.2.2 在兩個併發點均完成全部 20,000 個請求,且沒有錯誤。
複查結果再次呈現相同的版本差異,顯示此現象並非首次掃描獨有。其根本原因尚未確認,後續將針對連線處理、資源限制及相關版本變更進行調查。
📁 靜態檔案與動態 Zstd
Section titled “📁 靜態檔案與動態 Zstd”本組測試比較不同檔案大小及編碼方式下的服務吞吐量。
226 B 與 10 KiB 檔案每輪發出 500,000 個請求,2 MiB 檔案每輪發出 5,000 個請求。各列均採用兩輪量測的中位數,且沒有請求失敗。
單位:req/s。
| 檔案與編碼 | Pingclair main |
Pingclair v0.2.2 |
nginx 1.31.6 | Caddy 2.11.7 |
|---|---|---|---|---|
| 226 B(身分編碼) | 181,429 | 177,053 | 35,046 | 18,057 |
| 10 KiB(身分編碼) | 129,790 | 124,398 | 31,825 | 15,547 |
| 10 KiB + Zstd | 127,721 | 127,286 | 21,666 | 11,037 |
| 2 MiB(身分編碼) | 1,441 | 1,459 | 1,102 | 635 |
| 2 MiB + Zstd | 1,884 | 2,201 | 437 | 280 |
Pingclair 在本組所有靜態檔案測試中均取得最高或接近最高的吞吐量。
以 main 為例:
- 226 B 身分編碼: 吞吐量約為 nginx 的 5.18 倍、Caddy 的 10.05 倍。
- 10 KiB 身分編碼: 約為 nginx 的 4.08 倍、Caddy 的 8.35 倍。
- 10 KiB + Zstd: 約為 nginx 的 5.90 倍、Caddy 的 11.57 倍。
- 2 MiB + Zstd: 約為 nginx 的 4.31 倍、Caddy 的 6.73 倍。
動態 Zstd 壓縮是本次測試中差距較為顯著的工作負載之一。不過,效能差距並非固定倍率,而是隨檔案大小、壓縮設定與實際傳輸資料量而變化。
在 2 MiB + Zstd 測試中,v0.2.2 達到 2,201 req/s,高於 main 的 1,884 req/s,差距約 14.4%。這與較小檔案的測試趨勢不同,值得進一步分析。
這些結果衡量的是完整 HTTP 回應的處理能力,包含編碼、傳輸及伺服器開銷,而非單純的 Zstd 壓縮速度。
Traefik 不提供原生靜態檔案託管功能,因此未納入本組比較。
🧾 存取日誌開銷(Telemetry Tax)
Section titled “🧾 存取日誌開銷(Telemetry Tax)”存取日誌有助於問題診斷與流量觀測,但記錄、格式化及寫入日誌也會消耗運算資源。
本組以 226 B 靜態檔案為工作負載,比較停用存取日誌(A)與啟用 JSON 存取日誌(B)的吞吐量差異。
日誌寫入位於記憶體中的 tmpfs,藉此減少持久化儲存裝置的影響。
以下為兩輪量測的中位數,單位為 req/s。
| 伺服器 | A:無日誌 | B:JSON 日誌 | 吞吐量保留率 |
|---|---|---|---|
Pingclair main |
176,379 | 162,103 | 92% |
Pingclair v0.2.2 |
176,273 | 155,769 | 88% |
| nginx 1.31.6 | 37,256 | 35,095 | 94% |
吞吐量保留率定義為:
保留率 = 啟用日誌後的吞吐量 ÷ 停用日誌時的吞吐量
結果顯示,nginx 的相對日誌開銷最低,啟用 JSON 日誌後仍保留約 94% 的吞吐量。
Pingclair main 的保留率約為 91.9%,高於 v0.2.2 的 88.4%,提升約 3.5 個百分點。
換言之,main 的日誌開銷由舊版約 11.6% 降至 8.1%,顯示新版在這項工作負載下的日誌處理效率有所改善。
就絕對吞吐量而言,Pingclair main 啟用 JSON 日誌後仍達到 162,103 req/s,約為 nginx 的 4.62 倍。
需要注意的是,本組使用 tmpfs 作為日誌儲存位置,並未衡量持久化磁碟、遠端日誌收集或高負載日誌輪替的額外成本。
Caddy 未納入本組比較。
🌐 HTTP/3(實驗性)
Section titled “🌐 HTTP/3(實驗性)”本組測試使用 226 B 靜態檔案,觀察 HTTP/3 在頻繁建立與關閉 QUIC 連線時的吞吐量。
每輪量測持續 10 秒,並採用多輪結果的中位數。Pingclair 與 Caddy 各執行兩輪;由於 nginx 的輪間變異較大,額外增加至四輪。
單位:req/s。
| 伺服器 | req/s |
|---|---|
Pingclair main |
2,338 |
Pingclair v0.2.2 |
2,322 |
| nginx 1.31.6 | 1,857 |
| Caddy 2.11.7 | 1,400 |
兩版 Pingclair 的 HTTP/3 吞吐量相近,差距低於 1%。
在本組測試條件下,Pingclair main 的吞吐量約為 nginx 的 1.26 倍、Caddy 的 1.67 倍。
所有已完成的 HTTP 請求均成功。nginx 在部分測試輪次中出現少量連線層失敗,最高約占連線數的 2%;表列結果保留了這些失敗對實際吞吐量的影響。
工作負載說明
本組採用每條 QUIC 連線完成一個請求後立即重新建立連線的模式。
因此,測試結果主要反映 QUIC 連線建立、TLS 握手、連線關閉及 HTTP/3 請求處理的綜合成本,並不代表長時間重用同一條 QUIC 連線時的持續吞吐能力。
tquic_client 的 --total-requests-per-thread 0 是本組量測的必要參數。在使用的版本中,若省略此參數,客戶端會在首個請求完成後結束執行,無法形成預期的 10 秒測試視窗。
加入該參數後,-d 10 才能控制完整的量測時間。
HTTP/3 組仍屬實驗性測試。其絕對吞吐量受到負載產生器、QUIC 實作,以及 OrbStack 容器網路 UDP 行為的共同影響,應僅在本次環境與工作負載範圍內比較。
📐 結果的適用範圍與限制
Section titled “📐 結果的適用範圍與限制”容器與虛擬化環境
所有測試均在搭載 Apple M2、16 GB 記憶體的 MacBook Pro 上,透過 OrbStack Linux VM 執行。
測得的吞吐量不代表原生 Linux、FreeBSD、macOS 或其他硬體平台上的絕對效能,亦不構成生產環境的容量保證。
CPU 配額與並行模型
每個受測伺服器容器限制使用 2 顆 CPU,但保留各軟體預設的 worker 與執行緒配置。
由於執行緒模型、排程策略及資源管理方式不同,在其他 CPU 配額或硬體環境下,相對效能排名可能改變。
高併發掃描
掃描每個併發點僅發出 20,000 個請求,且只執行一次。
在數千併發的情境下,請求數量相對於連線數偏少,結果更適合用於觀察極端連線壓力下的行為,而非估計長時間穩態吞吐量。
連線失敗也可能涉及負載產生器、容器網路或作業系統的資源限制,不能僅憑錯誤率判定伺服器的根本問題。
壓縮設定
各伺服器使用自身的 Zstd 實作及指定壓縮設定。
由於壓縮策略及輸出資料量可能不同,Zstd 測試結果應理解為完整服務路徑的效能比較,而非相同壓縮工作量下的演算法效能排名。
HTTP/3 工作負載
HTTP/3 測試使用短生命週期的 QUIC 連線,不能代表長連線、多路複用或真實網際網路環境下的所有 HTTP/3 使用情境。
本次測試亦未涵蓋不同封包遺失率、網路往返延遲或頻寬限制下的表現。
未參與比較的項目
Caddy 未納入反向代理與日誌開銷組。
Traefik 不具備原生靜態檔案託管功能,因此未納入靜態檔案測試;本次亦未量測 Traefik 的 HTTP/3 表現。
缺席的項目不代表其效能高低。
版本與結果快照
本頁記錄的是 2026 年 10 月 11 日至 12 日期間取得的量測結果,涵蓋當時的正式發行版本及特定開發提交。
後續版本即使修正相關問題,也不會追溯改變本次測試數據。新的效能結論須以重新執行的量測為準。
🧾 測試方法與來源
Section titled “🧾 測試方法與來源”Pingclair 儲存庫提供基準測試的通用方法、有效性判定規則及相關測試工具;本次量測的完整方法(環境、候選版本、容器配置與命令)記錄於:
本頁列出的數值均為指定版本、測試環境與工作負載下的量測結果。比較應以相同測試條件為前提,不宜將不同平台、不同版本或不同負載下的結果直接合併解讀。
