訪問性能瓶頸定位與優(yōu)化:從指標(biāo)定義到實踐排查)
做LLM服務(wù)部署的人遲早都會撞上同一個問題單發(fā)請求的時候模型響應(yīng)又快又穩(wěn)一旦并發(fā)上來整個服務(wù)的表現(xiàn)就開始變得不可捉摸——有時候是吞吐上不去有時候是首字時延忽高忽低有時候干脆直接OOM崩潰。很多人第一反應(yīng)是“加GPU”或者“把并發(fā)參數(shù)調(diào)大”但如果不先把瓶頸定位清楚加卡和調(diào)參往往都只是在瞎猜。這篇文章把我這些年實際部署和排查LLM并發(fā)訪問性能問題用到的完整方法論拆開講從指標(biāo)定義、分層定位、壓測實操到常見故障速查給出一套可以直接復(fù)用的分析流程。內(nèi)容不挑框架不挑具體硬件核心思路是通用的適合正在做LLM服務(wù)落地、模型推理優(yōu)化或者平臺穩(wěn)定性保障的工程師參考。1. 先搞清楚并發(fā)場景下LLM服務(wù)到底慢在哪一層1.1 一個請求從進來到返回經(jīng)過哪些環(huán)節(jié)LLM服務(wù)的調(diào)用鏈路比普通Web服務(wù)長得多也復(fù)雜得多。一個請求從客戶端發(fā)出到最終返回完整回答至少要經(jīng)過這幾個環(huán)節(jié)網(wǎng)關(guān)和負(fù)載均衡層接收外部請求、做路由、限流、鑒權(quán)。這一層通常是普通的網(wǎng)絡(luò)服務(wù)單機扛幾千上萬個并發(fā)連接都沒問題但在高吞吐的LLM場景下網(wǎng)關(guān)的轉(zhuǎn)發(fā)延遲和排隊策略反而會成為第一個瓶頸。推理框架的調(diào)度層這是LLM服務(wù)特有的部分??蚣苁盏秸埱蠛笠獩Q定把它放在哪個worker上、什么時候放進Batch、排隊能容忍多久都由這一層負(fù)責(zé)。調(diào)度策略的好壞直接決定并發(fā)上限。模型推理引擎真正執(zhí)行模型計算的地方。一個請求的生命周期會拆成兩個階段——預(yù)填充prefill和逐Token生成decode兩個階段的計算特征完全不同瓶頸表現(xiàn)也完全不同。硬件資源層GPU扛著絕大部分計算CPU負(fù)責(zé)數(shù)據(jù)處理、結(jié)果采樣以及一部分與GPU通信的雜活內(nèi)存和顯存分別決定能放多少請求狀態(tài)和KV Cache。很多并發(fā)性能問題表面上看是“GPU不夠快”實際上瓶頸可能藏在第一層的排隊策略、第二層的調(diào)度邏輯、或者第四層的顯存分配上。這也是我一直強調(diào)的分析LLM并發(fā)瓶頸要按層次拆不要一上來就被GPU利用率數(shù)字牽著鼻子走。1.2 并發(fā)放大瓶頸的三種典型路徑并發(fā)本身不會制造問題它只會把原本存在的小問題放大成系統(tǒng)級故障。從實踐來看并發(fā)訪問下的性能劣化基本沿著三條路徑發(fā)生。路徑一是排隊效應(yīng)。服務(wù)端處理能力是固定的并發(fā)一旦超過處理上限請求就開始排隊。排隊帶來的不只是等待時間變長更麻煩的是尾時延爆炸——只要有幾個請求特別長整個系統(tǒng)的TP99和TP99.9都會變得很難看。很多時候并發(fā)上不去并不是計算能力不夠而是隊列管理策略不合理比如用了嚴(yán)格FIFO又沒有超時熔斷一個大請求把后續(xù)所有小請求都堵死。路徑二是資源爭搶。GPU的計算單元和顯存帶寬是共享的多個請求同時在跑必然要搶這些資源。LLM的decode階段本質(zhì)上是顯存帶寬密集型操作幾個長請求一起生成Token時帶寬會被迅速打滿這時候再增加并發(fā)每個請求的生成速度反而會一起掉下來。這種“越加越慢”的現(xiàn)象會讓很多人在指標(biāo)圖前懷疑人生。路徑三是框架和系統(tǒng)層的額外開銷。Python推理框架里的鎖競爭、請求級的內(nèi)存拷貝、CPU和GPU之間的同步等待、日志和指標(biāo)采集這些都是在并發(fā)上去之后會被顯著放大的開銷。我見過一個案例壓測到30并發(fā)時GPU利用率只有40%但CPU已經(jīng)跑到120%滿載最后發(fā)現(xiàn)瓶頸居然在框架里大量的JSON序列化和正則匹配上。這種問題加多少卡都沒用得先處理代碼層開銷。這三條路徑往往同時存在所以分析時要學(xué)會用組合指標(biāo)去拆而不是只盯著一兩個監(jiān)控圖下結(jié)論。下面先定指標(biāo)再講分層定位的實操方法。2. 指標(biāo)先行分析性能瓶頸前必須定的核心指標(biāo)2.1 吞吐與時延LLM場景的指標(biāo)口徑分析并發(fā)性能最忌諱的就是沒有統(tǒng)一指標(biāo)就開始調(diào)參。先說清楚LLM場景里幾個必須定義的指標(biāo)。吞吐TPS指系統(tǒng)每秒完成的請求數(shù)。很多人以為TPS等于并發(fā)數(shù)除以單請求耗時在普通Web服務(wù)里這個公式近似成立但在LLM服務(wù)里不成立——因為一個請求不是一次性算完的它會持續(xù)占用GPU資源幾十秒甚至幾分鐘。所以LLM服務(wù)更常用的口徑是“每塊GPU每秒生成的Token數(shù)”和“同時處理的請求數(shù)”前者反映系統(tǒng)的實際產(chǎn)出能力后者反映資源占用情況。這兩個指標(biāo)比單純的請求級TPS更能說明系統(tǒng)有沒有被有效利用。時延要拆成兩個來看。TTFT首Token時延是從請求發(fā)出到收到第一個Token的時間它主要由prefill階段和排隊延遲決定用戶感知最直接。TPOT每Token生成時間是相鄰兩個Token之間的間隔它決定首Token之后的生成速度也就是用戶看到的“打字機”節(jié)奏。單請求時TTFT可能只要200毫秒并發(fā)到50之后變成3秒這個變化趨勢比絕對值更值得關(guān)注。我給團隊定的基線經(jīng)驗是先壓出單請求時的TTFT和TPOT作為理論基準(zhǔn)然后以兩者不劣化超過50%為界限找到系統(tǒng)能承受的最大并發(fā)度。這個50%是經(jīng)驗值不同模型和硬件可以自己調(diào)整但一定要有一個量化標(biāo)準(zhǔn)否則你沒法判斷到底有沒有劣化、劣化了多少。2.2 并發(fā)度、隊列深度與飽和度除了吞吐和時延還有三個指標(biāo)容易被忽視并發(fā)度、隊列深度、飽和度。并發(fā)度指“同時在處理中”的請求數(shù)。注意它和QPS不是一回事QPS是每秒進來的請求數(shù)并發(fā)度是系統(tǒng)里正在跑的請求數(shù)。對LLM這種長耗時服務(wù)一個請求占著GPU資源幾十秒所以并發(fā)度的含義比QPS重要得多。系統(tǒng)里同時跑10個請求和100個請求GPU壓力和調(diào)度難度完全不同。隊列深度指排著隊還沒有開始處理的請求數(shù)。這個指標(biāo)是排隊效應(yīng)最直接的晴雨表。隊列深度持續(xù)增長且沒有回落趨勢說明系統(tǒng)的處理能力已經(jīng)到頂再壓下去只會徒增等待時間。飽和度指關(guān)鍵資源被用到的程度可以是GPU的計算單元占用率、顯存帶寬占比也可以是CPU某個核心的占用率。飽和度達到80%以上通常要警惕達到95%以上基本可以斷定該資源就是當(dāng)前瓶頸。我一直跟團隊說飽和度要找“第一滿”的資源因為系統(tǒng)里第一個被占滿的資源往往就是短板。2.3 指標(biāo)采集方案與壓測基線指標(biāo)定了怎么采也有講究。單機壓測階段建議至少采集四個層面的數(shù)據(jù)系統(tǒng)層CPU、內(nèi)存、磁盤IO、網(wǎng)絡(luò)連接數(shù)用系統(tǒng)自帶的監(jiān)控命令就能看注意區(qū)分空閑和等待兩種狀態(tài)。GPU層顯存占用、計算單元利用率、顯存帶寬利用率、溫度功耗用GPU廠商配套的監(jiān)控工具看。這里有個坑——顯存占用高不代表GPU在工作很多框架會把緩存占滿但計算單元利用率可能只有個位數(shù)??蚣軐优抨犝埱髷?shù)、正在處理的請求數(shù)、TTFT和TPOT的分位分布。這些通常要打開推理框架自帶的監(jiān)控接口才能拿到。應(yīng)用層業(yè)務(wù)自身的耗時日志、超時率、重試率。別小看這層很多時候問題已經(jīng)從服務(wù)端轉(zhuǎn)移到了客戶端比如客戶端超時設(shè)置太短導(dǎo)致大量重試把服務(wù)端活活壓垮。壓測基線建立也很重要。我一般先跑一個低并發(fā)基線比如1到2個并發(fā)得到單請求的最佳時延作為上限參考然后按2、5、10、20、50、100逐步加壓每個檔位穩(wěn)定跑3到5分鐘記錄穩(wěn)態(tài)指標(biāo)而不是毛刺峰值。壓測腳本里的請求內(nèi)容也要模擬真實場景混合不同輸入長度、混合短問答和長文生成因為純短請求和純長請求壓出來的結(jié)果差異非常大。2.4 組合指標(biāo)判別表快速定位瓶頸方向單個指標(biāo)說明不了問題指標(biāo)組合才能指向具體瓶頸。我把常見的組合和對應(yīng)結(jié)論整理成一張表排查時可以直接對照現(xiàn)象組合典型結(jié)論優(yōu)先排查方向GPU利用率高 顯存占用高 TTFT正常計算能力接近上限模型量化、精簡解碼邏輯、控制并發(fā)度GPU利用率低 顯存占用高 TPOT慢可能被緩存或調(diào)度卡住檢查KV Cache管理、批處理策略GPU利用率低 CPU利用率高瓶頸在預(yù)處理或框架開銷優(yōu)化數(shù)據(jù)處理、關(guān)掉多余日志TTFT暴漲 隊列深度持續(xù)增長處理速度跟不上請求速度降低目標(biāo)并發(fā)、優(yōu)化prefill批次TPOT正常 整體吞吐上不去調(diào)度層沒把請求喂飽檢查批大小上限、調(diào)度策略顯存沒滿但OOM顯存碎片化或預(yù)留不足開啟顯存整理、調(diào)整緩存上限這張表不絕對但能幫你在拿到一堆監(jiān)控圖之后快速圈定排查范圍少走彎路。接下來要做的就是沿著分層思路把瓶頸精確定位到某一個環(huán)節(jié)。3. 分層拆解從客戶端到GPU的瓶頸定位法3.1 入口網(wǎng)關(guān)與請求排隊最容易忽略的第一道關(guān)卡很多人排查LLM性能瓶頸時直接盯著GPU結(jié)果在網(wǎng)關(guān)層栽了跟頭。我印象很深的一次壓測時發(fā)現(xiàn)并發(fā)一高TTFT就開始猛漲GPU利用率卻不到30%查了一圈發(fā)現(xiàn)是負(fù)載均衡策略出了問題新進來的請求被輪詢發(fā)到多個worker上但每個worker自身的隊列已經(jīng)堆積了很多請求新請求只能排在隊尾等著。網(wǎng)關(guān)層的排查重點有三個一是負(fù)載均衡策略是不是按worker的實際負(fù)載來分發(fā)而不是簡單的輪詢二是隊列是不是無界隊列無界隊列在大流量下會無限堆積最終拖垮整個服務(wù)三是限流和熔斷有沒有生效沒有限流的話上游突發(fā)流量會直接擊穿服務(wù)。解決辦法也比較直接把負(fù)載均衡改成按隊列深度加權(quán)分發(fā)給隊列設(shè)置上限隊列滿了就直接返回過載響應(yīng)讓客戶端走重試策略。這里還要提醒一句網(wǎng)關(guān)層的連接數(shù)、線程池大小、超時時間都是配套的調(diào)了負(fù)載均衡策略但沒調(diào)超時時間可能會導(dǎo)致客戶端等不到結(jié)果就重試反而放大流量。3.2 框架調(diào)度與批處理策略連續(xù)批處理為什么是核心LLM推理框架的調(diào)度層最關(guān)鍵的機制是批處理策略尤其是連續(xù)批處理continuous batching。傳統(tǒng)做法是等一批請求全部結(jié)束再一起釋放這種靜態(tài)批處理在并發(fā)場景下效率很低——一批里只要有一個長請求其他短請求都得陪著等。連續(xù)批處理的核心思路是批次不固定只要某個請求生成完了就把它移出批次馬上補一個新請求進來。這樣GPU的計算單元始終有活干吞吐能提升不少。如果框架的批處理機制沒生效你會發(fā)現(xiàn)一個很典型的癥狀GPU計算單元利用率有波動但吞吐一直上不去而且請求越長整體吞吐越低。排查時要確認(rèn)三件事框架版本是否開啟了連續(xù)批處理批大小上限是否足夠大很多框架默認(rèn)批上限偏保守請求長度是否差異極大導(dǎo)致批次碎片化。批大小上限設(shè)置我要多說一句不是越大越好。批大小增大確實能提高GPU利用率但也會讓KV Cache占用快速增長而且一旦某個請求特別長它會拖住整個批次反而把TPOT拉長。調(diào)度策略里的另一個關(guān)鍵點是prefill和decode是否混跑。有些框架會把prefill和decode分開處理因為兩者計算特征差異很大prefill是計算密集decode是帶寬密集。分離調(diào)度能避免兩類請求互相干擾但實現(xiàn)復(fù)雜度高。如果框架支持這個選項值得開起來對比壓測一下很多時候能顯著改善長尾時延。3.3 推理引擎與算子熱點prefill和decode要分開看到了推理引擎這一層就必須要理解prefill和decode的本質(zhì)差異。prefill階段要把用戶的整個輸入一次性喂給模型計算量很大但對延遲的影響是線性的decode階段則是每次都只生成一個Token需要反復(fù)讀取模型參數(shù)計算量不大但受顯存帶寬限制嚴(yán)重。用GPU廠商提供的性能分析工具抓算子熱點時你會發(fā)現(xiàn)prefill階段的熱點集中在矩陣乘法算子bound在計算單元上decode階段的耗時大頭反而在線性層的小矩陣運算上因為每次計算的數(shù)據(jù)量很小但需要頻繁訪問顯存中的參數(shù)量和中間量。拿到算子級別的耗時分布之后優(yōu)化方向就清楚了prefill快就走量化或者精簡模型結(jié)構(gòu)decode慢就走算子融合、減少中間張量讀寫、或者用更高效的解碼實現(xiàn)。還有一個很容易忽略的點采樣和后處理算子。有些框架在每次decode之后要做logits處理、采樣、解碼標(biāo)記等操作這些在單請求時看不出來并發(fā)高了之后如果采樣邏輯寫得低效CPU端會成為瓶頸。用性能分析器如果抓到很多時間花在采樣相關(guān)算子可以考慮換更高效的采樣實現(xiàn)或者把采樣移到GPU上做。3.4 顯存與KV Cache并發(fā)越高越緊張的一塊資源LLM并發(fā)服務(wù)里顯存管理是重中之重而大頭就在KV Cache。KV Cache是用來緩存歷史Token的注意力鍵值對的它的大小和請求的上下文長度、并發(fā)度直接相關(guān)。直觀理解就是每個請求在生成過程中都要把已經(jīng)看過的內(nèi)容記下來并發(fā)請求越多、上下文越長占用的顯存就越大。顯存層面的瓶頸有兩個常見表現(xiàn)。一是KV Cache上限設(shè)得太小導(dǎo)致框架頻繁把歷史上下文丟掉或者強制截斷模型回答質(zhì)量下降不說還會觸發(fā)重新計算性能雪崩。二是顯存碎片化嚴(yán)重多個請求不斷進出KV Cache的內(nèi)存塊被拆得七零八落明明剩余總量夠但就是分不出連續(xù)的大塊來于是報OOM。解決碎片化問題的思路是采用分頁式緩存管理把KV Cache切成固定大小的塊按需分配或者定期做顯存整理、重啟回收。排查顯存瓶頸時不要只看“顯存占用率”這一張圖要看框架暴露的緩存命中率、緩存重算率、以及請求級的顯存分配日志。緩存重算率一旦超過5%基本可以斷定KV Cache管理有問題需要調(diào)緩存策略或者換框架版本。另外長上下文請求對顯存的放大效應(yīng)也要提前評估別拿平均長度去預(yù)估容量要拿P95甚至P99的長度算否則并發(fā)稍高就會被長請求打爆顯存。3.5 CPU-GPU協(xié)作與通信開銷多卡場景更容易踩坑當(dāng)模型被切到多塊GPU上做并行推理時通信開銷會成為新的瓶頸來源。GPU之間的數(shù)據(jù)交換走高速互連每傳輸一次張量都有固定的延遲和帶寬成本。并發(fā)上來之后多個請求同時觸發(fā)通信互連帶寬會被迅速打滿各卡的計算單元反而在空轉(zhuǎn)等待數(shù)據(jù)。排查多卡通信瓶頸主要看兩個數(shù)據(jù)單卡在執(zhí)行計算等待通信的時間占比以及互連鏈路的帶寬占用率。如果算力等待通信的時間占比超過30%就要考慮減少通信次數(shù)——比如把需要頻繁交換的層合并成更大的通信塊或者調(diào)整并行切分方式。這里有個代價權(quán)衡并行度切得越細(xì)單卡顯存壓力越小但通信開銷越大。實際部署中并行度不是越高越好要拿同一條壓測曲線對比不同并行度下的吞吐和時延找到交叉點。對于7B級別模型很多時候單卡就能跑強行切多卡反而得不償失只有模型大到單卡放不下或者單卡算力不足時多卡并行才有意義。4. 實操記錄一次典型的并發(fā)性能劣化排查過程4.1 壓測環(huán)境與腳本準(zhǔn)備光講方法論有點虛我拿一個實際發(fā)生過的排查案例來走一遍完整流程。模擬項目X的背景是某個內(nèi)部知識庫問答服務(wù)接入了LLM單塊數(shù)據(jù)中心級GPU部署模型是7B規(guī)模的對話模型推理框架用的是某開源推理框架最大上下文長度設(shè)置為8K。業(yè)務(wù)方抱怨白天高峰期經(jīng)常出現(xiàn)“轉(zhuǎn)圈很久才出第一個字”偶爾直接返回錯誤。第一步先搭壓測環(huán)境。壓測工具選用開源壓測工具腳本里模擬了三種請求短問答輸入50個字輸出200個字、中等問答輸入500個字輸出500個字、長文總結(jié)輸入2000個字輸出800個字比例按真實流量設(shè)為3:4:3。并發(fā)梯度設(shè)為1、5、10、20、30、50每個檔位穩(wěn)定壓5分鐘記錄指標(biāo)。壓測之前我特意做了件小事把框架的詳細(xì)訪問日志打開記錄每次請求的到達時間、排隊開始時間、prefill開始時間、decode開始時間和結(jié)束時間。實踐證明這個舉動價值巨大后面幾乎所有排查結(jié)論都是從這些時間點里算出來的。4.2 第一輪QPS上不去先看是不是GPU真的滿了低并發(fā)基線先跑出來的數(shù)據(jù)并發(fā)1時TTFT約180毫秒TPOT約35毫秒也就是每秒大約28個Token這個數(shù)字符合7B模型在這張卡上的正常水平。并發(fā)加到5一切正常TTFT略漲到220毫秒吞吐在漲。并發(fā)加到10奇怪的事情出現(xiàn)了QPS幾乎不再增長穩(wěn)定在每秒鐘完成3到4個請求左右TPOT從35毫秒漲到了48毫秒但GPU計算單元利用率只有55%左右。這就出現(xiàn)了我之前說的“GPU沒滿但性能上不去”的典型情況。按組合指標(biāo)判別表方向鎖定在框架調(diào)度層。進一步看框架日志里的時間點發(fā)現(xiàn)請求從到達開始到真正進入批次平均等待了1.8秒排隊時間比prefill時間還長。原因很快浮出水面框架默認(rèn)的最大批大小是32但decode階段的批次實際上一直沒有填滿因為請求長度差異大很多短請求早就結(jié)束批次里面剩下的都是長請求新請求補充不進來。這是我第一輪得到的結(jié)論瓶頸在連續(xù)批處理的填充效率不在GPU算力。4.3 第二輪提高并發(fā)后顯存OOM問題在KV Cache把批大小上限從32調(diào)到64同時打開框架的連續(xù)批處理開關(guān)重新壓測。這次QPS確實漲了從3.5到了一定水平但并發(fā)加到20的時候直接出現(xiàn)了顯存OOM整個worker重啟線上服務(wù)斷了十幾秒。查看顯存監(jiān)控記錄發(fā)現(xiàn)進程退出前顯存占用率只有75%不是滿了才崩的而是分配KV Cache時找不到連續(xù)的大塊顯存。這就是典型的顯存碎片化問題。解決方法是調(diào)整框架的緩存策略把KV Cache改成按固定塊分配同時限制單個請求的最大上下文長度避免個別長請求一次性申請超大塊顯存。調(diào)完之后并發(fā)20下OOM消失顯存使用穩(wěn)定在85%左右。這輪排查的教訓(xùn)是OOM不一定等于顯存總量不夠很可能是分配策略的問題。遇到OOM先別急著加顯存先看框架日志里申請的大小和位置再決定是調(diào)整緩存策略、限制上下文長度還是真需要升級硬件。4.4 第三輪單請求正常但并發(fā)后TTFT抖動劇烈OOM解決之后繼續(xù)加壓并發(fā)到30時出現(xiàn)了新的癥狀TTFT的中位數(shù)還算穩(wěn)定但TP99從300毫秒直接飆到5秒以上而且抖動劇烈忽快忽慢。GPU計算單元利用率還是沒到80%看起來不像算力瓶頸。把請求時間點日志拉出來按階段聚合發(fā)現(xiàn)抖動集中在prefill階段當(dāng)一批請求里同時進來幾個長文總結(jié)請求時prefill批次的計算量瞬間暴漲把后續(xù)請求的prefill都堵住了。推理框架默認(rèn)把同一時間到達的請求放進一個批次做prefill如果這批請求都是長輸入就產(chǎn)生了“長請求堵短請求”的問題。解決辦法是開啟框架的prefill和decode分離調(diào)度選項prefill請求攢夠一定數(shù)量才啟動避免一個大prefill批次卡住所有decode請求同時設(shè)置prefill批次的輸入token總量上限超過就拆分。調(diào)整之后并發(fā)30下TTFT的TP99從5秒降回到1.2秒抖動明顯改善。4.5 定位結(jié)論與優(yōu)化落地把三輪排查的結(jié)論匯總下來第一輪發(fā)現(xiàn)連續(xù)批處理填充低效第二輪發(fā)現(xiàn)KV Cache碎片化第三輪發(fā)現(xiàn)prefill和decode互相干擾。這三個問題都不是“GPU不夠快”而是框架調(diào)度和顯存管理的配置問題。最終優(yōu)化方案總共四項打開連續(xù)批處理批大小上限調(diào)到64。KV Cache改為塊級分配并設(shè)置上限限制單請求最大上下文。開啟prefill/decode分離調(diào)度限制prefill批次的總輸入量。網(wǎng)關(guān)層加隊列上限和超時熔斷防止上游突發(fā)流量把框架壓垮。優(yōu)化后重新壓測并發(fā)50時QPS比初始版本提升了約2.6倍TP99時延從5秒下降到1.4秒GPU計算單元利用率從55%提升到接近80%。整個過程沒有換硬件沒有改模型純靠定位分析就把性能挖出一大截。這個案例可以說明一件事LLM并發(fā)性能問題的根因絕大多數(shù)時候不在模型本身而在模型周圍的調(diào)度和資源管理。5. 高頻問題與排查技巧速查5.1 并發(fā)上去了吞吐反而下降這是LLM服務(wù)最經(jīng)典的反直覺現(xiàn)象。核心原因通常是decode階段的顯存帶寬被打滿所有請求同時在讀模型參數(shù)帶寬分不過來每個請求的生成速度都下降。排查時先看GPU帶寬利用率再看TPOT是不是隨并發(fā)同步上漲。如果是帶寬問題方向是降低并發(fā)上限、用量化模型減小每次讀取的數(shù)據(jù)量、或者優(yōu)化算子減少重復(fù)讀參。也有可能是批處理策略導(dǎo)致的靜態(tài)批處理下批次里一旦有長請求整個批次釋放慢新請求遲遲進不來宏觀表現(xiàn)就是并發(fā)越高吞吐反而下降。這時候要果斷確認(rèn)連續(xù)批處理是否真的在生效。5.2 顯存沒滿但報OOM顯存總量剩余很多卻報OOM九成是碎片化問題一成是框架內(nèi)存池管理Bug。解決問題的第一步永遠是看框架日志OOM時申請的內(nèi)存塊多大、最大可用連續(xù)塊多大。如果是碎片化就改用塊級緩存分配方式或者定時做顯存整理。另外大批量KV Cache申請和釋放是碎片產(chǎn)生的根源限制單請求上下文長度能在源頭減少超大的連續(xù)申請。5.3 Token生成速度遠低于預(yù)期單個請求TPOT明顯慢于理論值時先別懷疑GPU型號不行。按經(jīng)驗順序排查是不是模型量化位寬太高導(dǎo)致解碼效率低是不是采樣階段做了太多CPU和GPU之間的同步是不是框架在decode階段插了額外的安全檢查或日志操作是不是其他請求在共享GPU帶寬用GPU監(jiān)控工具確認(rèn)一下即可。從實踐看TPOT異常多數(shù)是同步和調(diào)度問題并非真正的算力問題。5.4 時延分布長尾嚴(yán)重TP99和TP50差距懸殊說明存在少數(shù)請求被拖住的“堵車”現(xiàn)象。最常駐的三類原因一類是長輸入請求占據(jù)了prefill批次后續(xù)短請求被迫等待一類是請求排隊位置靠后趕上系統(tǒng)微突發(fā)排隊時間過長還有一類是個別慢請求觸發(fā)了超時重試重試流量又疊加進系統(tǒng)。定位方法就是分階段統(tǒng)計時延分布看漂移出長尾的到底是排隊、prefill還是decode階段然后對癥下藥。5.5 多卡擴展后性能不升反降多卡并行不是免費的午餐。模型切分粒度太細(xì)、通信次數(shù)太多、或者互連帶寬本身不夠都會導(dǎo)致每加一張卡通信開銷吃掉的收益比加卡帶來的算力增量還多。排查時重點看通信時間占比和互連鏈路帶寬占用率。我見過不少案例是模型不大但強行把并行度拉滿結(jié)果性能比單卡還差。這里的經(jīng)驗是模型能在單卡放下且算力夠用就不要切多卡必須多卡時對比不同并行度下的實際壓測數(shù)據(jù)再做決定。5.6 排查工具清單與日常監(jiān)控建議最后整理一份我常用的排查工具清單按用途分類列一下系統(tǒng)層監(jiān)控操作系統(tǒng)自帶性能監(jiān)控命令重點看CPU的user、system、wait三類時間占比。GPU層監(jiān)控GPU廠商監(jiān)控工具重點采集計算單元利用率、顯存占用、帶寬利用率、溫度功耗。進程級分析系統(tǒng)自帶的進程追蹤工具排查CPU綁定和上下文切換開銷。算子級分析GPU廠商性能分析套件和推理框架自帶的性能分析器生成算子耗時火焰圖??蚣苤笜?biāo)接口推理框架自帶的監(jiān)控接口采集排隊數(shù)、處理中請求數(shù)、TTFT/TPOT分布、KV Cache使用率。日常監(jiān)控我建議至少保留這五塊看板QPS和并發(fā)度、TTFT和TPOT的中位數(shù)/TP99、排隊深度、GPU利用率與帶寬利用率、KV Cache和顯存使用率。不要貪多五塊看板配合告警閾值足夠覆蓋90%的并發(fā)性能問題。告警閾值可以參考前文說的基線方法以單請求最優(yōu)值的1.5倍作為告警線持續(xù)超過3分鐘就觸發(fā)排查。結(jié)尾說實話分析LLM并發(fā)訪問性能瓶頸這件事真正難的不是用什么高級工具而是有沒有一套清晰的拆解思路。我踩過不少坑之后最大的體會是永遠先量化再優(yōu)化先分層再定位先解決調(diào)度和顯存問題再考慮換硬件。很多團隊一遇到并發(fā)性能差就申請加卡結(jié)果加完發(fā)現(xiàn)GPU利用率還是上不去錢白花了。如果你現(xiàn)在正被類似問題困擾建議先花半天時間把框架訪問日志打開、把各項指標(biāo)壓測基線跑出來再對照這篇文章的排查流程走一遍。最后再分享一個小技巧每次壓測之后把結(jié)論寫成一段話存檔標(biāo)上“現(xiàn)象-根因-改動-效果”積累一段時間之后你會發(fā)現(xiàn)自己判斷瓶頸的速度越來越快很多問題看一眼監(jiān)控圖就知道出在哪一層了。