GB10 上每秒五十萬請求:瓶頸是網路,不是那台機器
我們想知道雲端資料平面在 NVIDIA GB10 上能跑多快。得到的教訓比較像網卡接收佇列,不太像我們自己的程式碼——同一條線上,吞吐量還是超過原本的兩倍。

本頁內容
雲端建在單一 Go 二進位檔上。一份成品同時掛上 AI 閘道、商務、IAM 與 KMS,對外提供 api.hanzo.ai。HTTP 這一面是 zip,我們原生於 ZAP 的 Web 框架,包在 fasthttp 外面。於是我們問了一個很直的問題:這東西實際上能有多快,又是什麼讓它停下來?
老實的答案驚了我們三次。下面是整段追查,數字一併附上。全部公開在 hanzoai/benchmarks。
其一:框架算不算那筆稅
不算。zip 蓋在它所包的原始 fiber/fasthttp 引擎上,只多出固定的大約 24 奈秒,以及一次 48 位元組的配置——任何真實處理函式都不滿 1%。從頭到尾、在回送迴路上,zip 做出原始 fasthttp 的 0.95×。框架只是雜訊。好,往下走。
| 框架 | 請求/秒 |
|---|---|
| 原始 fasthttp | 365–400k |
| zip (HTTP) | 338–348k |
其二:分散之後的結果反而更慢
有趣的地方從這裡開始。兩台機器以一條直連的 2.5 GbE 網路線相接:伺服器是 spark(一台 NVIDIA GB10,20 個核心),負載產生器是 evo(32 核心的 x86)。把負載挪出這台機器,spark 的核心理當空出來專心服務——我們以為數字會往上。
它往下了。194k 請求/秒。再加連線、再用力推,情況更糟:4,000 條連線是 167k,8,000 條是 154k,p99 延遲從 5 ms 爬到 52 ms。這就是教科書裡的壅塞崩潰。
可是鏈路只有約 ~70 MB/s,不過是 2.5 GbE 的 22%。不是頻寬。spark 那 20 個核心也大多閒著。不是 CPU。那到底是什麼?
元兇:一條接收佇列
ethtool -l 把底揭開了:網卡當時只以一條硬體 RX 佇列在跑。每個進來的封包都由一個核心的 softirq 處理——那個核心就是天花板。更糟的是,驅動程式乾脆拒絕加佇列(ethtool -L → "Operation not supported")。
於是我們最快的開發機,被一條鏟封包的核心執行緒掐住,另外十九個核心在一旁看著。
修法:用軟體把封包導開
硬體不肯多佇列,核心會肯——接收封包導向在軟體裡把流量雜湊到各個核心。我們在全部 20 個核心打開 RPS(以及 RFS/XPS),把接收積壓從 1,000 拉到 300,000,插座緩衝設成 256 MiB,並釘住效能調速器。一支冪等的 tune.sh,兩台機器都跑。
同一條線。同一個處理函式。每秒 518,000 次請求——跳了 2.67×,峰值 696k。
| 並行度 | 未調整 | 調整後 |
|---|---|---|
| 1,000 | 194k | 518k |
| 4,000 | 154k | 474k(峰值 696k) |
關鍵在這裡:調整後的網路數字(518k)現在超過回送迴路(365–400k)——線上的 spark 核心全時服務,不必再跟負載產生器分攤。518k 時機器大約只閒 ~12%。這已經貼近單一載入器所能頂到的真實天花板。
其三:ZAP 本來該更快——所以我們把它變快了
zip 原生於 ZAP:一個光禿的監聽位址說我們的二進位傳輸;http:// 說 HTTP。我們以為二進位傳輸會贏,做了一個 ZAP 負載用戶端(HTTP 工具說不了它),然後量。
ZAP 是 91k。HTTP 是 481k。原生傳輸輸了——輸了 5×。
追下去才發現不是線,是編解碼器。延遲的尾巴把故事說完(p50 健康,在 303 µs,可是 p99 在 13 ms——垃圾回收的簽名),把 GOGC 從 100 放到 800,ZAP 不只翻倍,到了 203k。六秒的一輪裡,ZAP 伺服器觸發了 2,203 次垃圾回收;HTTP 伺服器 57 次。三個配置點扛著它:編組器每次呼叫新鑄一塊影格緩衝,標頭解碼把每條字串複製進一張對照表,還有最大的一處——zaphttp 的伺服器依請求配置全新的請求情境,而 fasthttp 自己的伺服器是依連線放進池子的。
所以我們修了。這次改動(zap-proto/http,分支 perf/zero-alloc-codec)是機械的,不是架構的:請求情境按連線池化一次,手寫一個往池化緩衝追加的影格編碼器,並用 bufio.Peek/Discard 抽掉 4 位元組長度前綴,讓標頭陣列不逃到堆積上。服務迴圈用 AppendResponse 序列化進那塊重複使用的緩衝,並就地解碼標頭——於是在一條熱連線上,它每個請求什麼都不配置——黃金測試確認線上逐位元組相同,改前改後都一樣。純粹重寫編解碼器,協定沒動。
結果:ZAP 走到 91k → 570k 請求/秒,抬升 6.3×,超過 HTTP 的 481k——回送迴路上 ZAP 現在以 1.18× 領先,p99 從 13 ms 塌到 1.19 ms,GC 從 2,203 降到 62。
有兩件事我得自己改正,而且修完它們仍然成立,所以值得講明白。第一,贏的是 CPU,不是那條線。 ZAP 影格實際上比 HTTP 更大(一次 /health 請求是 124 對 75 位元組);ZAP 贏,是因為每個位元組花的週期更少,不是因為位元組更少。第二,zaphttp 並沒有跳過 JSON。 它在兩種傳輸上都把 HTTP 本文當成不透明位元組來帶,所以處理函式兩邊付同樣的編組成本——在對話形狀的 JSON 酬載上,ZAP 實際上是 HTTP 的 0.95×,把本文複製進影格尾端帶來一點損失。略去 JSON 是原生 ZAP 具型 RPC 的性質,是 zaphttp 碰不到的另一條軸。這裡的回送勝利是誠實的,純粹是 CPU 與配置的勝利。那真正的 2.5 GbE 線上呢?兩種傳輸在 ~530k 打平——鏈路飽和了,編解碼器不再是瓶頸。
其四:那麼 ZAP 真的跳過 JSON 嗎
最後那一句——「略去 JSON 是原生 ZAP 的性質,zaphttp 並不行使它」——是一張借據,讀者應當讓我兌現。zaphttp 把本文當不透明資料帶著走,所以它從不檢驗二進位協定理當贏的那一點:不解析文字就讀出結構化資料。於是我做了做這件事的測試。
一筆共用紀錄——形狀像 eth_getBalance 的呼叫,每個方向四個欄位(account、block、id → balance、nonce、blockHash)——以四種方式來回:標準函式庫的 encoding/json、goccy/go-json 與 sonic(Go 裡最快的兩個 JSON 函式庫),以及 zap-proto/go 的具型二進位。進去的欄位相同,計算相同,出來的欄位相同;來回等價測試強制逐位元組的再編碼,誰也不能省掉對方正在做的工作。而且我把 JSON 按在它最快的實作上,不是標準函式庫,免得有人揮手把結果打發掉。
解碼是一場屠殺。 讀這個四欄位請求:
| 編解碼器 | 解碼 | 配置次數 |
|---|---|---|
| JSON 標準函式庫 | 2334 ns | 13 |
| JSON sonic | 845 ns | 8 |
| JSON goccy | 473 ns | 4 |
| 具型 ZAP | 13 ns | 0 |
十三奈秒,零次配置——比最快的 JSON 快 35×,比標準函式庫快 174×。 沒有解析:欄位坐在已知偏移上,你去讀它。這就是具型二進位線路的全部主張,而且它站得住。
但我答應過,不替 ZAP 說好話的部分也要報,所以:
- 編碼是平手,而且它配置得更多。 ZAP 用 629 ns、12 次配置完成序列化,goccy 是 426 ns / 4。已發布的
zap-proto/gov1.3.0 建構器把變長尾端透過一次複製往後推,並讓物件建構器逃到堆積上;修好它的池化定寬 API 不在已發布的標籤裡,所以這就是老實的數字。軋差之後,完整的伺服器處理函式(解碼 + 計算 + 編碼)的確偏向 ZAP——274 ns 對 goccy 的 431——因為解碼的勝利淹沒了編碼的平手。但扛著的是解碼。 - 從頭到尾,174× 縮到大約 ~1.2×。 在真實的
/rpc端點上:ZAP 535k 請求/秒,sonic 483k(1.11×),goccy 452k(1.18×),標準函式庫 407k(1.31×)。是真的,但離微基準的差距很遠——因為在每秒五十萬次請求時,插座和排程器才是成本,序列化只是薄薄一片。編解碼器早在你察覺它變快之前,就不再是瓶頸了。
有一個意外倒向 ZAP 這一邊:在真實的結構化紀錄上,它的線路更小,不是更大——162 對 233 位元組——因為 JSON 把位元組花在 0x 十六進位字串、十進位整數、引號和欄位名稱上。成框測試裡「ZAP 更大」的保留,只對微不足道的 /health 尺寸酬載成立;給它真正的具型欄位,二進位在尺寸上也贏。
所以,老實說:ZAP 跳過 JSON 嗎?跳過——而且在讀取路徑上,差距並不接近。 你感不感覺得到,取決於序列化是不是你的瓶頸。每秒 500k 的小紀錄,它不是。換成肥厚的巢狀酬載,或解碼很重的扇出,那個 174× 就會出現在你過日子的地方。
我們學到的
- 量整條路徑,並質疑工具。
hey把我們卡在大約 ~100k——那是用戶端,不是伺服器。bombardier露出了多 4× 的東西。 - 機器很少是第一處瓶頸。 一條網卡 RX 佇列,在我們碰應用程式碼之前,就拿走了 2.67×。
- 量配置,不只是吞吐量。 ZAP「慢」離明顯是編解碼器問題只差一個
GOGC旗標——一旦我們把情境放進池子並把配置清零,原生傳輸就超過了 HTTP。 - 微基準和負載測試回答的是不同的問題。 具型解碼單獨比 JSON 快 174×,從頭到尾快 1.2×——兩個數字都是真的,只引其中一個就是用省略說謊。
- 公平地、按實況報告。 同一個處理函式,熱連線,不挑 GOGC,JSON 按在最快的函式庫上而不是最慢的,把指令稿公開。老實的數字是一次失利時——編碼的平手、被壓縮的端到端差距——我們也把失利交了出去。
零配置的 ZAP 編解碼器做完了,而且它贏。剩下的停點是物理的:雙網卡載入器(通往七位數的並行接收路徑)、更快的互連(10 GbE / RDMA)、一次 writev 拆分,讓 ZAP 的影格尾端在大酬載上省掉一次本文複製,以及 Go 1.26 原生交給我們的後量子傳輸——PQ-TLS 1.3、ZAP-over-PQ-TLS 與 PQ-QUIC——我們將在那裡把 ML-KEM 交握的成本從穩態吞吐量裡分開。
需要走多遠,我們就能走多遠。
繼續閱讀

Hanzo DNS and Networking: Programmable Infrastructure
Launching Hanzo DNS and Networking — programmable DNS with service discovery and zero-trust virtual networks.

BitDelta:從單一圖形處理器叢集提供 14 個 Zen 模型
BitDelta 以 1 位元增量壓縮,讓我們從共用的圖形處理器基礎建設提供 14 個 Zen 模型變體——數學、架構與取捨。

從商業智慧到前沿模型:Hanzo 十年來的基礎架構
打造 AI 基礎架構的 12 年。自 2014 年的即時推薦引擎起,到 2026 年的 94+ 前沿模型與後量子區塊鏈。這是我們走到這裡的技術紀錄。