在 Zenoh 的發展過程中,曾經深入比較過幾個 Rust 的非同步框架,其中還經歷了Runtime選擇的轉換async-std -> tokio。
以下筆者先帶各位讀者回顧之前的研究報告,
其結果很有意思:async-std 在延遲和吞吐量上表現最好,甚至壓過了 Tokio 和 smol。特別是在 CPU 密集或網路競爭激烈的情境下,差距就更明顯。
Zenoh 本身就是用 Rust 開發的,目標很明確:
要做到 超低延遲 與 高吞吐量 的通訊能力,非同步框架就是最關鍵的基礎之一。
測試方式是透過 ping-pong 應用程式,量測在 localhost 與 100GbE 網路下的往返延遲(RTT)。同時測試 純網路負載 以及 混合計算負載,模擬 Zenoh session 中高達 1000 個並行任務的真實情境。
之前的實驗距今已經三年了,套件都早已更新好幾版,究竟最新的測試結果會不會不一樣呢?
以下是筆者更新後的net-benchmark 腳本,並在本機進行了新的網路測試。
這套基準工具是一個 統一的 ping-pong 網路測試器,支援多個 Rust 非同步後端(async-std
、Tokio
、smol
)以及標準函式庫(std
)。它可以量測 TCP/UDP 的往返延遲,並可自由設定封包大小與傳送頻率。
亮點功能:
--backend tokio|async-std|smol|std
)Pong(接收端)
Ping(發送端)
這樣的設計能精確量測 RTT,並在不同 runtime 框架之間進行公平比較。
# 使用 async-std 跑一個 TCP pong 伺服器
cargo run -- --mode pong --protocol tcp --backend async-std --address 127.0.0.1:5555 --size 64
# 使用 async-std 跑一個 TCP ping 客戶端
cargo run -- --mode ping --protocol tcp --backend async-std --address 127.0.0.1:5555 --size 64 --interval 0.001 --duration 10
let now = Instant::now();
stream.write_all(&payload).await.unwrap();
stream.read_exact(&mut payload).await.unwrap();
let elapsed = now.elapsed();
samples.push(elapsed);
這段程式基本展示了 ping 的核心迴圈:
同樣的邏輯也套用在所有後端與協定上。
以下是 64-byte 封包 的結果:
頻率 | async-std | smol | std | tokio |
---|---|---|---|---|
10 Hz | 340.75 | 317.84 | 229.98 | 296.08 |
100 Hz | 239.11 | 243.60 | 238.42 | 265.34 |
1 KHz | 31.42 | 31.14 | 25.43 | 112.44 |
10 KHz | 22.30 | 23.77 | 23.37 | 36.18 |
無限 Hz | 14.43 | 14.33 | 11.00 | 35.31 |
觀察下來:即使在一台普通筆電上,async-std 和 smol 在高頻場景下延遲依然更低;而 Tokio RTT 偏高,跟之前的測試一致。
雖然 async-std 在效能上表現亮眼,但 Zenoh 最終還是轉向 Tokio,原因如下: