4

軟體

依據 benchmark 與 profiling 選擇 runtime 和 kernel。

10 分鐘閱讀

印刷頁: 93–116

在 GitHub 加星

一口氣讀懂

推論軟體是一座逐層提高抽象、逐層減少直接控制的階梯。Compute Unified Device Architecture(CUDA)kernel 決定工作如何接觸 NVIDIA 硬體;framework 描述 tensor 程式與序列化方式;inference engine 把最佳化 kernel、scheduling、batch、cache 及分散式設定包成可部署服務;協調層再管理多個 worker。實作時應從能滿足需求的最高層開始,只有量測證明某個能力缺口或昂貴瓶頸存在,才往下一層深入,且每次改動都要回到符合正式流量形狀的負載驗證。

為何重要

  • 較高抽象層可以縮短交付時間,也把社群累積的最佳化經驗包在預設值裡,但同時會隱藏 kernel 與 scheduling 選擇。較低層提供精細控制,代價則是架構專屬程式、相容性維護與更大的測試面積。團隊必須知道目前交換了哪些控制權,才不會在錯誤層級解決問題。
  • 數學上完全相同的操作,可能因資料分塊、讀寫順序與硬體對應方式不同,而產生截然不同的記憶體流量與運算使用率。因此 kernel 選擇與融合不是改變演算法,而是把抽象模型轉成適合特定硬體的實作。
  • Inference engine 讓團隊不必重做 continuous batching、cache 管理、quantization 路徑、speculation 與平行執行等基礎設施。可是 engine 的優劣仍取決於模型架構、GPU 世代、流量形狀與團隊控制需求,沒有一個選項能在所有部署上永遠最快。
  • Benchmark 回答服務到底有沒有變快,profiler 則回答時間與記憶體花到哪裡。需要因果細節時若缺少第二層證據,最佳化就只是接連提出聽起來合理的故事;若缺少端到端 benchmark,局部改善也可能無法轉成使用者真正感受到的成果。

心智模型

把每一層看成一份介面契約:硬體公開指令與記憶體,CUDA 啟動具體 kernel,PyTorch 等 framework 描述 graph 與 tensor,檔案格式保存 weights 或執行 graph,engine 選擇實作並 scheduling 請求,orchestrator 則把工作 route 到不同 replica。效能工程要問的是哪一份契約擋住下一步,然後只改動能解決問題的最窄一層,避免為了單一瓶頸而接管所有下層複雜度。

inference software stack - 2026 年 1 月版快照

硬體先承載 CUDA kernel,PyTorch 與模型格式建立在 CUDA 之上,inference engine 再組合最佳化服務,而 Dynamo 位於最上層協調分散式部署中的多個 engine。

  1. accelerator 硬體提供運算、記憶體與 interconnect
  2. CUDA 與 kernel明確控制 GPU 執行與記憶體
  3. PyTorch 與模型格式描述 graph、tensor、編譯與序列化
  4. Inference engine組合 scheduling、batch、kernel 與最佳化參數
  5. 分散式協調層處理 routing、cache 重用與 worker 協調
  • accelerator 硬體CUDA 與 kernel: 公開裝置能力
  • CUDA 與 kernelPyTorch 與模型格式: 支撐上層運算
  • PyTorch 與模型格式Inference engine: 提供模型與 graph
  • Inference engine分散式協調層: 成為後端服務

新模型發布的第一天,最容易看見整個堆疊的相依關係。Reference library 可能已能下載 config 與 weights,也能用簡單程式跑出正確結果,但正式 engine 尚未理解新的 attention 形式;接著 engine 的 nightly build 可能支援 graph,最關鍵的最佳化 kernel 卻只針對舊 GPU;再往下,driver 或 CUDA 版本也可能與 container 內其他套件衝突。不要把單一「可載入」視為正式支援,而要沿著檔案格式、模型定義、framework operation、kernel、engine、driver 與硬體一路畫出完整支援鏈。先找到第一組輸出正確、負載穩定、可重現的組合,把版本及映像固定下來,再從這個可信基點最佳化。若為了 day-zero 支援採用 prerelease 套件,也要準備回退路徑,並把升級後的相容性與品質測試納入交付條件。

選 inference engine 要先排除不符合硬限制的候選:模型架構與 modality 是否支援、accelerator 與精度是否有 kernel、授權是否符合產品、必要的 continuous batching、quantization、cache、speculation、parallelism 與 disaggregation 路徑是否存在。接著比較營運適配度,包括官方 container、設定表面、記錄與監控、升級節奏、除錯工具,以及團隊是否能承擔它的複雜度。只有通過這兩層篩選的 engine,才值得用完全相同的模型版本、流量分布與品質套件做 head-to-head benchmark。支援面廣的選項可能以較短時間上線,深度專門化的選項可能以更高工程成本換取更多容量。最終比較應把延遲目標下的吞吐量、GPU 數量、維護投入與故障風險換算成服務成本,而不是用一張脫離約束的峰值圖表宣布冠軍。

有效的效能實驗是一個封閉循環。先固定環境、隨機種子能固定的部分與基線設定,再重播接近正式環境的序列長度、抵達間隔、concurrency 分布、temperature 與 prefix 重複。先看端到端 percentile latency、吞吐量、錯誤與品質;只有結果不足以解釋時,才深入 profiler,把時間歸因到 CPU、GPU、memory copy、kernel 或 interconnect。一次改一個 kernel 或參數,執行足夠次數,確認改善超過正常波動,也沒有讓輸出品質或記憶體安全退步。之後再與其他預定最佳化合併測試,因為各自有效的設定可能共同爭用資源。最後 shadow 正式流量,證明合成測試結論仍成立。這份循環留下可重現證據,也讓 dependency 更新後能判斷是重新調校、維持舊版或安全回退。

Kernel fusion 清楚展示抽象邊界為何影響效能。兩個單獨看都很快的 kernel,如果中間必須產生大型 tensor、寫回 video random-access memory(VRAM)後立刻重讀,組合起來仍可能受頻寬限制。規則清楚的 graph 可由 compiler 自動融合;成熟 engine 可能依 shape 與 GPU 選擇已知 fused plugin;少見架構則可能需要 framework 貢獻者依 profiler 證據手寫實作。越往低層修改,就要負責越廣的相容矩陣:矩陣形狀、精度、alignment、GPU 世代、錯誤輸入與未來版本都要測。保留正確的 unfused fallback,讓不支援的組合仍可服務;再以真實 scheduling、batch 與多請求負載確認端到端收益。只有局部記憶體流量減少確實轉成使用者延遲或系統容量改善,fused path 才值得長期維護。

分散式 orchestrator 應該回答已量測的需求,而不是因為架構圖看起來完整就加入。若多個 replica 之間有大量 prefix 可重用,協調層可把請求送到持有相符 cache 的 worker;若 prefill 與 decode 的負載曲線不同,它可以獨立擴縮兩組資源;若模型工作真的跨越節點,也需要統一管理 topology 與狀態。缺少這些需求時,直接使用 inference engine 會少一層 queue、網路 hop、版本相容與故障模式。評估協調層不能只測穩態峰值,還要製造不均勻流量、cache cold start、worker 中斷、擴縮轉換與下游變慢,觀察 routing 是否維持延遲目標、狀態是否遺失、queue 是否無界增長。只有這些動態情境下的收益大於操作成本,新的抽象層才成立。

每一個最佳化決策都要保存成可查核證據:原始假設、完整 dependency 與硬體版本、模型 revision、traffic generator 設定、原始分布而非只有平均值、品質結果、profile trace、選定參數、被淘汰方案及 rollback trigger。軟體迭代很快,半年後 engine 預設值、compiler 能力或 kernel 支援可能已經改變;沒有紀錄時,團隊無法分辨某個手動 plugin 是已失去必要性的 workaround,還是仍在保護重要限制,也容易把一次過時 benchmark 變成口耳相傳的定律。升級前重跑同一套 evidence package,比單純確認服務能啟動更能揭露效能與品質退步;發生事故時,完整決策記錄也讓團隊能快速回到最後一組已知安全設定。

核心概念

CUDA 元件

Kernel 負責執行平行工作;CUDA graph 記錄 kernel 與其他裝置操作,以便高效重播;driver 是連接硬體的低階介面;runtime 則是供開發者啟動 kernel 與管理記憶體的 application programming interface(API)。CUDA 程式通常以 C++ 撰寫,再編譯成主機端與裝置端程式碼。

先用 kernel library,再考慮自製

Basic Linear Algebra Subprograms(BLAS)是 cuBLAS 矩陣原語的基礎,cuDNN 則涵蓋神經網路運算。CUTLASS 與 CuTe 提供可組合的 template,FlashInfer 提供推論 kernel。多數團隊應先讓 engine 自動選擇,或插入經過驗證的現成 kernel;只有關鍵路徑長期穩定、量測收益足以支付維護成本時,才值得手寫新實作。

選擇與硬體緊密綁定

針對某一 GPU 世代、記憶體配置或矩陣形狀調校的 kernel,換到另一代可能無法利用新資源,甚至不能執行。自動編譯器與 engine 能處理常見路徑;手動 plugin 則必須同時證明相容範圍、正確性、fallback 與實際效益。

Framework 與格式

2026 年 1 月版快照:PyTorch 是主要可程式化 framework。Safetensors 把不含可執行程式的 weights 與架構分開保存,Open Neural Network Exchange(ONNX)則可把 weights 和執行 graph 包在一起交給可攜式 runtime,但能否匯出仍取決於操作與資料結構支援。

參考 library 不等於正式服務

2026 年 1 月版快照:Hugging Face transformers 與 diffusers 適合查看模型定義、設定、下載工具、notebook,以及釐清輸入輸出行為;面對正式流量時,通常仍要使用編譯後的 PyTorch 程式或具備 scheduling 與記憶體管理的 serving engine。

Engine 選擇

2026 年 1 月版快照:vLLM 著重易採用與廣泛模型支援;SGLang 著重可替換元件,以及 mixture of experts(MoE)與 diffusion 路徑;TensorRT-LLM 著重深度 NVIDIA 最佳化,但設定成本較高。這是一張當時的地圖,不能取代目前版本的實測。

協調層有規模門檻

2026 年 1 月版快照:Dynamo 協調 engine,以支援 cache-aware routing、prefill 與 decode 分離,以及 multi-node 工作。大型模型與高流量可能從中受益;小型服務若沒有足夠 cache 重用或獨立 worker pool,額外分散式層反而只是負擔。

負載與 benchmark 工具

2026 年 1 月版快照:SGLang GenAI-Bench 與 NVIDIA GenAI-Perf 針對生成式模型流量,Locust 則提供一般 concurrency 負載。Eval dataset 可同時提供寫實輸入與品質抽查,但請求分布仍必須對齊正式環境。

Profiling 工具

2026 年 1 月版快照:PyTorch Profiler 涵蓋 framework 步驟,NVIDIA Nsight Systems 追蹤 CPU、GPU 與 interconnect 活動,Nsight Compute 則分析單一 kernel。選擇能解釋 benchmark 結果的最窄範圍即可。

運作機制

  1. 選一個支援目標模型的 engine,固定模型版本、精度、硬體、dependency image、請求形狀與 concurrency 分布,建立可重複的基線。調速度前先確認輸出品質,否則後續數字無法判斷是否用正確性換來。
  2. 優先複製正式請求做 shadow test,讓測試系統接收真實負載卻不影響原始回應。若只能模擬,就要重現輸入輸出長度、請求內容、抵達抖動、concurrency 量、sampling 設定及會影響 cache hit 的重複模式,不能只送整齊的合成提示。
  3. 一次只改一個變數,送入足夠流量並重複多次,降低 outlier 左右結論的機會;之後再把個別有效的改動合併測試。不同技術可能爭用同一批資源,所以單項增益不能直接相加。
  4. 當端到端結果不足或與預期矛盾,再深入 profile CPU 時間、GPU 時間、記憶體、kernel duration 與 interconnect 活動。先把成本歸因到最大且可避免的步驟,再決定是否值得往較低抽象層修改。
  5. 面對頻寬密集的操作序列,檢查相鄰操作是否把中間結果寫回記憶體後立刻重讀。簡單情形先交給 compiler fusion;只有重要、穩定而且自動工具無法涵蓋的路徑,才使用需要長期維護的手寫 fused kernel。
  6. 插入選定 kernel 或新設定後,必須回到原始端到端 benchmark。單一 kernel 變快不代表服務改善;只有使用者延遲、系統吞吐量、單位成本或可承載容量在品質不退步的條件下變好,改動才成立。
unfused 與 fused path 的記憶體流量

unfused 讀取與第一個 kernel 會導向中間結果來回,穿過 video random-access memory(VRAM);fused path 讀取則經過單一 fused kernel,直接到最終寫入。

unfused 讀取
從 VRAM 載入輸入
第一個 kernel
計算中間結果
中間結果來回
寫回 VRAM 後再讀取
第二個 kernel
計算最終數值
fused path 讀取
只載入一次輸入
fused kernel
一次完成兩個操作
最終寫入
只儲存一次輸出
  • unfused 讀取第一個 kernel: 輸入
  • 第一個 kernel中間結果來回: 額外流量
  • 中間結果來回第二個 kernel: 重新載入
  • fused path 讀取fused kernel: 輸入
  • fused kernel最終寫入: 輸出

關鍵指標

P50、P90 與 P99 time to first token(TTFT)

TTFT

依輸入長度與負載切分,才能看見 queueing 與 prefill 的變化。

單一請求的 tokens per second(TPS)

TPS

要連同 batch 與 concurrency 量報告,不能只列脫離服務條件的最大值。

系統吞吐量

tokens/s 與 requests/s

同時說明達到該吞吐量時遵守的延遲限制。

Operator 與 kernel 時間

ms 與 trace 占比

用 profile 尋找昂貴運算、記憶體搬移、launch 空檔或主機端工作。

尖峰與配置記憶體

GB

修改編譯或 kernel 時,分開追蹤模型、cache、workspace 與暫時配置。

輸出品質差異

eval 分數

把接近正式用途的評估輸入同時當成 benchmark 流量與最佳化退步護欄。

取捨

便利性與極致控制

支援面廣的 engine 減少實作時間並涵蓋許多模型;當模型、硬體與流量足夠穩定,專用或更低階路徑才可能用較大的工程投入換得更高效能。

自動編譯與 plugin

編譯器能替一般操作自動選 kernel 並融合,custom kernel 則處理少見或高度調校路徑。Plugin 也可能切斷編譯器的全局視野,並擴大版本與硬體相容工作。

純 weights 與可攜 graph

Safetensors 讓序列化單純、安全,執行由程式定義;ONNX 能攜帶 graph 跨 runtime,但複雜模型特性可能因匯出規格不支援而失敗。

直接 engine 與 orchestrator

中小型部署直接使用 engine 較簡單;只有 cache routing、獨立 worker pool 或 multi-node 協調能實質改善大型服務時,分散式協調層才值得它的額外成本。

快速測試與忠實測試

小型均勻負載能快速回饋,卻可能獎勵錯誤設定;符合正式分布的測試耗時較長,但能避免序列、負載、cache 與參數不一致誤導決策。

工程檢查表

  • 選擇堆疊前,依目前官方 release note 重新查核每項 2026 年 1 月版支援與版本敘述。
  • 固定 driver、CUDA、framework、engine、kernel plugin、模型 revision 與 container image,讓比較可以重現。
  • 第一次最佳化前保存通過品質檢查的基線,並完整保留它的流量設定。
  • 對齊序列長度、concurrency 量、抵達模式、請求內容、sampling 參數與預期 cache 行為。
  • 一次只測一項改動,重複執行、檢查 percentile,再測試多項技術是否彼此干擾。
  • 只有決策需要因果細節時才深入 profile,並選對範圍:framework、全系統或單一 kernel。
  • 手動 kernel 必須驗證 GPU 世代、shape、精度、正確性、fallback 行為與端到端效益。
  • 最終設定以 shadow 或 canary 上線,持續觀察延遲、吞吐量、記憶體、錯誤與輸出品質。

術語

CUDA kernel
編譯後在 NVIDIA GPU 上平行執行工作的函式。
CUDA graph
可重複使用的 GPU 操作有向 graph,用來降低反覆 scheduling 的額外成本。
General matrix-matrix multiplication
模型 linear layer 反覆使用的線性代數操作,也是 kernel 選擇的重要目標。
Kernel fusion
合併相鄰操作,讓中間結果留在運算附近,避免不必要的記憶體來回。
Safetensors
只保存 tensor 資料、不含可執行 Python 物件,且支援 memory mapping 的 weights 序列化格式。
Inference engine
把模型執行、scheduling、batch、記憶體管理與最佳化控制包在一起的服務 runtime。
Traffic shadowing
把正式請求複製到測試部署,但不改變原始呼叫者看到的回應。
Profiler
把耗時與資源使用歸因到操作、kernel、主機工作或通訊的工具。

原書索引

  • 第 93-101 頁

    抽象堆疊、CUDA、kernel library、選擇與融合。

  • 第 101-105 頁

    Framework、編譯、序列化格式、可攜 runtime 與參考 library。

  • 第 105-111 頁

    2026 年 1 月版的 serving engine 能力與定位。

  • 第 111-112 頁

    2026 年 1 月版的分散式協調方式與規模門檻。

  • 第 112-114 頁

    代表性負載、benchmark 工具與實驗紀律。

  • 第 114-116 頁

    Profiling 範圍、工具與從量測回到實作的循環。

Software abstraction stack, CUDA components, kernel selection, and fusion
印刷頁: 93–101; PDF 頁: 95–103
PyTorch, compilation, model formats, runtimes, transformers, and diffusers
印刷頁: 101–105; PDF 頁: 103–107
vLLM, SGLang, and TensorRT-LLM capabilities and selection
印刷頁: 105–111; PDF 頁: 107–113
NVIDIA Dynamo orchestration, scale threshold, cache routing, and disaggregation
印刷頁: 111–112; PDF 頁: 113–114
Production-shaped benchmarking, load generation, and controlled experiments
印刷頁: 112–114; PDF 頁: 114–116
Profiling purpose, tools, and benchmark-profile optimization loop
印刷頁: 114–116; PDF 頁: 116–118