2026/10/22   INFINITIX Product Dep.

前言:如何回答「這台伺服器能同時服務多少人?」

團隊在一台 GPU 伺服器上部署了開源大型語言模型(LLM)。你用 curl 送出一段 prompt(輸入提示),一秒多就開始回傳文字,看起來很快。但「看起來很快」並不能回答以下這些問題:系統能同時承受 10 人的請求,還是 200 人的請求?使用者在幾秒內看到第一個回覆文字出現才算合格?以串流方式顯示回覆文字的速度要多快才算流暢?

要回答以上這類問題,就需要進行 LLM benchmark(效能基準測試)。本文不介紹工具安裝,而是聚焦測試背後的邏輯:該量測哪些指標、為什麼不能只看單一數字、該如何施加負載,以及哪些細節可能讓測試結果失真。文中並會以 NVIDIA 開源的 AIPerf 工具為例,藉此說明這些概念在實際操作中該如何呈現。

一、什麼是 LLM Benchmark?

「LLM benchmark」常見有兩種意思。能力評測 benchmark(例如 MMLU、HumanEval 等題庫)關心的是模型答得對不對,而不是在評估硬體效能;推論效能 benchmark則關心同一個模型部署在特定硬體與推論引擎後,回應速度有多快,以及可以同時服務多少使用者。本文討論的是後者,也就是大家真正想知道的事。

不過,這類測試不能直接照搬一般 HTTP 壓力測試的做法,因為 LLM 推論和傳統 API 有幾個重要差異:

  • 回應會以串流方式逐個 token 傳回。第一個 token(Time to First Token,TTFT)何時出現、後續 token 間隔(Inter-Token Latency,ITL)多久,以及整段回答(End-to-End Request Latency,E2E Latency)何時完成,是三種不同的時間。
  • 每個請求的工作量不固定。模型可能在不同時間點停止生成;輸出越長,通常占用 GPU 的時間也越久。如果兩次測試的輸出長度分布不同,吞吐量就難以直接比較。
  • 運算分成 prefill 與 decode 兩個階段。Prefill 會處理整段輸入 prompt;decode 則逐步產生輸出 token。兩個階段的資源需求和效能瓶頸不同。
  • 不同請求會互相影響。推論引擎可能透過 continuous batching(連續批次處理),把多個請求的 decode 工作合併排程。因此,單一請求產生 token 的速度,也會受到同一時間其他請求的影響。
  • GPU 記憶體可能先成為瓶頸。每個尚未完成的請求都會占用 KV cache,需求也會隨序列長度增加。當記憶體接近或達到上限時,延遲可能突然惡化,而不是逐步變慢。

一次 benchmark 至少要先固定五件事:測試目標(API 服務端點 endpoint、模型、是否啟用串流)、工作負載(prompt 來源、輸入序列長度 (Input Sequence Length,ISL ) 與輸出序列長度 (Output Sequence Length,OSL))、負載模型(同時有多少請求、以什麼節奏送出)、量測指標,以及判定標準 SLO(Service Level Objective,服務等級目標)。把它當成受控實驗,一次只調整一個變因,才容易判斷結果改變的原因。

二、為什麼不能只看單一數字

2.1 同一條時間軸上的四個核心指標

所有指標都來自同一條時間軸:t₀ 是送出請求的時間,t₁ 是收到第一個 token 的時間;之後會陸續收到更多 token,直到 tₙ 收到最後一個 token。

AI請求流程時間軸

圖 1 從用戶端(client)觀察串流請求的時間軸,以及四項核心指標各自對應的時間區段

  • TTFT(time to first token)= t₁ − t₀。這段時間包含網路往返、伺服器排隊,以及 prefill 處理完整段 prompt 的時間。它反映使用者是否感覺到「系統有回應」,對 prompt 長度相當敏感,也常在並發量升高時率先惡化。
  • ITL(inter-token latency)= (request latency − TTFT) ÷ (OSL − 1)。這是收到第一個 token 之後,平均每隔多久收到下一個 token,能反映 decode 輸出的流暢度。TPOT 是業界常見的相關名稱。分母使用 OSL − 1,是因為第一個 token 的等待時間已計入 TTFT。
  • Request latency = tₙ − t₀。這是整段回答的總耗時。即使兩個請求的總耗時相同,使用者體驗也可能差很多:一個可能「等很久、但後面輸出很快」,另一個則是「很快開始、但慢慢輸出」。因此,總耗時必須搭配 TTFT 和 ITL 一起看。
  • 每位使用者的輸出速度 = 1 ÷ ITL。這表示單一使用者每秒大約能收到多少 token。若速度降到每秒個位數 token,使用者通常會明顯感覺輸出偏慢。

2.2 吞吐量:系統的整體產能

  • Benchmark duration:從第一個請求送出,到最後一個回應收到為止的時間。計算吞吐量時,會以這段時間作為分母。
  • Output token throughput = 所有輸出 token 的總數 ÷ duration。代表整個系統每秒總共產生多少輸出 token(Tokens Per Second,TPS)。
  • Request throughput = 完成的請求數 ÷ duration。代表系統每秒完成多少個請求(Requests Per Second,RPS)。它與 token 吞吐量之間的換算,取決於平均每個請求的輸出長度。

最容易混淆的例子,是「每位使用者每秒收到 20 個 token」和「整個系統每秒產生 2,000 個 token」。這兩個數字代表不同的事情,變化趨勢也可能相反:並發量提高時,batching 可能讓系統的總產出增加,但每個 decode 步驟也可能變慢,導致單一使用者的輸出速度下降。看到 tokens/s 這個單位時,記得先確認它指的是「每位使用者」還是「整個系統」。

2.3 為什麼不能只看平均值

延遲分布通常呈現右偏、帶有長尾的樣子:大多數請求很快完成,但少數請求可能因排隊或記憶體壓力而慢上好幾倍。平均值容易被這些慢請求拉高,卻無法清楚呈現尾端延遲的程度。AIPerf 方法論文件中的例子顯示,p99 比平均值高出七成以上。一般來說,p50 可用來觀察典型請求的延遲;p95 代表 95% 的請求延遲不超過這個值;p99 則代表 99% 的請求延遲不超過這個值,只有最慢的 1% 落在其後。SLO 通常會以 p95 或 p99 作為門檻。因此,好的測試工具會保留每個請求的原始紀錄,方便事後重新計算不同的百分位數(percentile)。

2.4 Goodput:同時衡量速度與服務品質

延遲與吞吐量往往互相牽制。要回答「系統能承受多少使用者」,就得把兩者放在同一個判準下看:goodput = 同時符合所有 SLO 的請求數 ÷ duration。例如,可以設定 TTFT < 500 ms 且 ITL < 50 ms;只有所有條件都達標的請求,才會計入 goodput 的分子。因此, goodput 不會高於 request throughput。容量規劃若選錯指標,結果可能差很多:AIPerf 文件中的例子顯示,以 throughput 估算需要 38 台伺服器,以 goodput 估算則需要 135 台。後者才真正代表「工作做得完,而且使用者體驗也符合要求」。

指標公式代表的意義/關注面向
TTFTt₁ − t₀使用者是否感覺到系統有回應
ITL(TPOT)(request latency − TTFT) ÷ (OSL − 1)輸出文字是否流暢
Request latencytₙ − t₀批次工作、Agent 流程
Output token throughputΣ OSL ÷ duration營運成本、GPU 利用率
Goodput符合 SLO 的請求數 ÷ duration容量規劃與服務品質

2.5 為什麼關閉串流後,就量不到 TTFT 與 ITL

如果請求沒有啟用串流,伺服器會等整段回答完成後才一次回傳。此時,client 端的時間軸只看得到 t₀ 與 tₙ:第一個 token 和最後一個 token 會隨整段回答一起到達。如此一來,TTFT 就會等同於 request latency,ITL 的計算也失去意義。因此,TTFT、ITL 與每位使用者的輸出速度都是只適用於串流輸出的指標(AIPerf 將它們標示為 streaming-only)。反過來說,如果產品本來就不使用串流,例如後端批次摘要,就不一定需要關注這些指標。選擇指標時,應以產品實際的使用方式為準。

有兩個設定特別容易讓量測結果失真。第一,輸出長度沒有控制好:模型可能提早遇到結束符號而停止生成,導致實際 OSL 短於預期,進而影響吞吐量與 ITL。較嚴謹的測試方式,是透過 ignore_eos 或 min_tokens 等參數,讓模型依設定產生足夠的 token。另須留意,有些服務仍使用 max_tokens 這個欄位名稱,也可能靜默忽略不支援的參數。第二,tokenizer 設定錯誤:client 端會透過 tokenizer 計算 ISL 與 OSL;若使用的 tokenizer 與模型不一致,所有以 token 為單位的指標都可能產生偏差。

三、負載模型:施加負載的方式,決定你量到什麼

負載模型不只是設定一個「大小」,更重要的是決定請求何時送出、以及依照什麼規則送出的機制。

閉環、開環負載模型的差異

圖 2 閉環(固定並發數)與開環(固定到達率)兩種負載模型的差異

Concurrency(固定並發數,閉環):模擬 N 位虛擬使用者,每位使用者送出一個請求,等收到完整回應後,再送出下一個。任何時候,尚未完成的請求數都不會超過 N。這種方式有回饋效果:系統愈慢,下一個請求就愈晚送出,因此整體送出速率會自動下降。它適合量測「N 位使用者持續使用時」系統能維持的穩態容量,結果較穩定、容易重現,但對突發流量來說,負載相對溫和。

Request rate(固定到達率,開環):每秒依照排程送出 λ 個請求,不管前一個請求是否已完成。若系統處理不及,尚未完成的請求就會不斷累積,延遲也可能快速上升。這種方式更接近固定速率到達的實際流量,適合驗證「預期尖峰下,延遲是否仍符合 SLO」。不過,它對 λ 的設定非常敏感:略低於系統可承受的容量時可能一切正常,略高時排隊就可能持續累積。

這兩種方法回答的是不同問題:「最多能同時服務多少人?」適合用閉環測試;「每秒收到 50 個請求時,p99 延遲是多少?」則適合用開環測試。常見的誤用是:閉環測試顯示「100 並發時,TTFT 的 p99 只有 800 ms」,上線後每秒湧入 100 個請求,系統卻可能撐不住。原因是當系統變慢時,閉環測試中的 100 並發,每秒實際送出的請求可能只有 30 個。

請求到達模式:開環測試還要決定請求間隔多久。Constant 模式會每隔 1/λ 秒送出一個請求,間隔完全固定;Poisson 模式的請求間隔則服從指數分布,平均間隔同樣是 1/λ,但會自然出現請求集中抵達與暫時沒有請求的空檔。這是模擬獨立使用者隨機送出請求的數學模型,也是 AIPerf 的預設模式。Constant 模式下,請求間隔固定,佇列較不容易突然堆積;Poisson 模式則可能在同一秒湧入五個請求,進而形成延遲長尾。因此,只用 constant 測得的 p99,通常會比貼近真實流量的測試結果樂觀。

3.1 掃描負載與 knee 轉折點

單一負載點只能告訴你「系統在這個負載下的表現」。若要回答「系統最多能承受多少壓力」,就要逐步提高並發數,例如依對數尺度測試 1、2、4、8……,並在每個負載點執行完整的 benchmark,再繪製吞吐量與延遲曲線。

Token吞吐量與TTFT延遲曲線

圖 3 並發掃描的吞吐量與延遲曲線:吞吐量先趨於飽和,延遲則可能急遽上升(圖左);與 SLO 門檻的交點可用來判定容量(圖右)

隨著並發數增加,吞吐量通常會持續上升,但增幅逐漸縮小,到某個點後可能持平,甚至略微下降。延遲則可能在一開始變化不大,超過某個點後開始急遽上升。這段轉折區域稱為 knee(轉折點)。較理想的工作點通常落在 knee 之前不遠處。若在圖上加上 SLO 的延遲門檻線,延遲曲線與門檻線的交點,就能用來判定「符合 SLO 的最高並發數」;再搭配該負載下的 goodput,就能更完整地回答有關運行效能的問題。

3.2 暖機與避免測試互相干擾

暖機:剛啟動的推論服務,前幾十秒的表現可能與穩定運作時差很多,原因包括載入模型權重、首次編譯 CUDA kernel、配置 KV cache 記憶體池,以及建立連線。AIPerf 方法論文件中的例子顯示,前 10 秒的 TTFT 比穩態高約 41%。若把這段時間也算進統計,p99 就可能比穩態實際表現更差,而且影響程度還會隨 benchmark 執行時間而改變。較好的做法是先執行一段 warmup(暖機測試),將這段請求標記為暖機資料並排除,再開始正式量測。

避免同時執行多組測試:假設兩位工程師各自對同一個 endpoint 執行 benchmark,並各自設定 32 並發,伺服器實際承受的可能是 64 個尚未完成的請求。這些請求共用同一張 GPU、同一套 batch 排程與同一池 KV cache,因此兩份報告中的延遲都可能比單獨測試 32 並發時更差,卻不容易從個別報告看出原因。因此同一個 endpoint 的 benchmark 應一次只執行一組;開始前就要確認沒有其他測試同時執行,而不是只靠口頭提醒。這個原則也適用於產生負載的 client 機器:如果 CPU 資源不足、可用連接埠耗盡,或事件迴圈被 JSON 解析工作卡住,量到的延遲也會混入 client 端的瓶頸,無法單純代表伺服器效能。

另外還有三個因素會影響結果的可比較性。第一是樣本數:每個負載點最好至少收集數百個請求,p99 才較有參考價值;只有一百個請求時,p99 幾乎就是最慢的那一筆。第二是工作負載特性:合成資料(固定 ISL/OSL)能提供可控制、可重現的容量數字;真實對話紀錄的長度分布則常帶有長尾,可能暴露合成資料看不到的問題。兩種都值得測試,但不適合直接比較。第三是可重現性:固定隨機種子可以讓 prompt 內容與 Poisson 到達間隔重現,但延遲本身仍有自然波動。比較兩種配置時,建議各跑數次並比較中位數,同時記錄模型、推論引擎版本、硬體、tokenizer、負載模型、暖機設定與 SLO 門檻,否則日後很難重現同一份報告。

四、AIPerf:這些概念如何出現在實際工具中

AIPerf 是 NVIDIA Dynamo 團隊開源的 LLM 推論效能 benchmark 工具,採用 Apache 2.0 授權,也是 GenAI-Perf 的後繼工具。本文不介紹操作流程,而是透過三項設計,對照前面提到的概念。

AIPerf架構

圖 4 AIPerf 的三個平面:控制平面決定負載模式,資料平面負責量測,分析平面負責統計

多程序與 credit(請求許可):AIPerf 會讓約十個服務分別執行在獨立程序中,並透過 ZeroMQ 溝通。整體分為控制平面(決定何時送出多少請求)、資料平面(由 worker 實際發送 HTTP 請求並記錄時間戳),以及分析平面(計算指標、匯出結果)。AIPerf 的負載控制核心之一是 credit,可以把它理解成一張「請求通行證」:worker 取得 credit 後才能送出一個請求,沒有 credit 就不能送。固定並發模式下,Timing Manager 一開始發出 N 張 credit;每收回一張,才補發一張,這就是閉環。Request rate 模式則依 Poisson 到達排程發放 credit,不必等前一張歸還,這就是開環。換句話說,負載模型的核心,就是決定「在什麼條件下發出下一張通行證」。

時間戳與 tokenizer:Worker 會記錄請求送出的時間,以及每個串流 chunk 抵達用戶端的時間。TTFT、ITL 與 request latency 都是透過時間戳相減計算;benchmark duration 則依整段測試實際經過的時間計算。ISL 與 OSL 由 tokenizer 計算,因此執行 benchmark 時必須指定 tokenizer,而且要與伺服器實際載入的模型一致。

掃描與自適應搜尋:格點掃描預設會測試八個以對數尺度分布的並發點:1、3、7、19、52、139、373、1000。每個點也可以重複測試多次,估計結果的信賴區間。如果想直接找出「符合 SLO 的最高並發數」,可以使用 max-concurrency-under-sla 這類 search recipe。預設演算法會先逐步提高負載,直到找到第一個超出門檻的點,再把可能的邊界縮小到一個區間;接著透過等張回歸(isotonic regression)降低量測雜訊、插值估算交點,並在邊界附近補做測試,直到區間窄於 5%。整個流程大約需要 13 到 25 次迭代。相較之下,若只測固定的八個點,可能只能判斷容量邊界落在 139 到 373 之間。如果目標改成「在符合門檻的條件下,找出最大吞吐量」,AIPerf 則可採用貝葉斯最佳化(Bayesian optimization):以高斯過程(Gaussian process)建立「並發數對應吞吐量」的預測模型,每次挑選預測表現較好或不確定性較高的點進行測試;若連續多次沒有改善,就停止搜尋。當需要探索的參數維度增加時,這種方法相較於固定格點掃描會更有優勢。

結語

運行效能的問題要先改寫成可以量測、驗證的形式,才有辦法回答:「在指定的模型、推論引擎與 ISL/OSL 工作負載下,採用閉環並發掃描,並排除暖機資料後,能讓 TTFT 的 p95 低於 X ms、且 ITL 的 p95 低於 Y ms 的最高並發數是多少?對應的 goodput 又是多少?」最後可以用一張吞吐量與延遲曲線、一條 SLO 門檻線,以及兩者的交點來呈現答案。

開始測試前,先確認以下幾件事:

  1. 是否啟用串流?這會決定能否量測 TTFT 與 ITL。
  2. 是否控制輸出長度?tokenizer 是否與模型一致?
  3. 要回答系統容量,使用閉環並發掃描;要驗證尖峰流量下的延遲,使用開環固定到達率。開環測試可採用 Poisson 到達模式。
  4. 是否排除暖機請求?同一個 endpoint 是否有其他 benchmark 正在執行?
  5. 除了平均值,也要看 p95、p99 等百分位數;除了 throughput,也要看 goodput。完整測試設定與每筆請求紀錄都應保存。

工具會更新、GPU 會更換,但這份檢查清單仍然適用。

參考資料

  • AIPerf 原始碼與文件:https://github.com/ai-dynamo/aiperf(包含 architecture、metrics-reference、comprehensive-llm-benchmarking、timing-modes-reference、search-recipes 等文件)
  • Yu et al., “Orca: A Distributed Serving System for Transformer-Based Generative Models”, OSDI 2022(continuous batching)
  • Zhong et al., “DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving”, OSDI 2024 (goodput)