優(yōu)與部署關(guān)鍵實(shí)踐:從指標(biāo)到實(shí)戰(zhàn))
1. 先搞清楚推理性能調(diào)優(yōu)到底在調(diào)什么做AI模型部署的同學(xué)應(yīng)該都有同感模型訓(xùn)練完了只是第一步真正讓人頭疼的是后面那句“上線跑一下”。訓(xùn)練時(shí)你關(guān)心loss降沒降、收斂快不快可一旦進(jìn)入推理階段畫風(fēng)就全變了——延遲、吞吐、顯存占用、首token耗時(shí)、并發(fā)能力每一個(gè)指標(biāo)都有可能成為壓垮線上服務(wù)的最后一根稻草。我見過不少團(tuán)隊(duì)把大量精力花在訓(xùn)練調(diào)參上結(jié)果模型一到推理階段就各種翻車GPU利用率上不去、請求一多就超時(shí)、顯存莫名其妙OOM?;仡^復(fù)盤問題往往出在“不知道推理性能的瓶頸到底在哪”這件事上。所以這篇文章想認(rèn)真聊一聊AI模型推理性能調(diào)優(yōu)與部署建議把我在實(shí)際項(xiàng)目中踩過的坑、驗(yàn)證過的方法、以及一些可以直接抄作業(yè)的配置方案分享出來。先給這篇文章定個(gè)位如果你正準(zhǔn)備把訓(xùn)練好的模型部署成線上服務(wù)或者已經(jīng)在部署但性能不理想這篇文章值得你從頭看到尾。內(nèi)容覆蓋推理性能指標(biāo)拆解、常見瓶頸定位、模型優(yōu)化手段、部署架構(gòu)選擇、運(yùn)行時(shí)參數(shù)配置、實(shí)測調(diào)優(yōu)過程以及高頻故障的排查思路。我會(huì)盡量用實(shí)際場景說話而不是堆理論。注意文中的所有測試數(shù)據(jù)和配置參數(shù)都來自我本人在某公司內(nèi)部搭建的模擬項(xiàng)目X中的實(shí)操記錄硬件環(huán)境為單卡和雙卡兩種方案結(jié)論在同類GPU上具備一定的參考價(jià)值但不同架構(gòu)、不同驅(qū)動(dòng)版本下結(jié)果會(huì)有差異建議以實(shí)測為準(zhǔn)。2. 推理性能的核心指標(biāo)與瓶頸拆解2.1 別只看單次延遲這些指標(biāo)同樣重要很多剛接觸部署的同學(xué)習(xí)慣拿“單次推理耗時(shí)”來衡量模型快不快但真實(shí)線上場景里單次推理耗時(shí)只是表象。舉個(gè)很簡單的例子一個(gè)接口單次推理只要10ms但只能同時(shí)處理1個(gè)請求另一個(gè)接口單次推理要30ms卻能同時(shí)處理32個(gè)請求。在業(yè)務(wù)并發(fā)較高的時(shí)候后者的整體吞吐反而是前者的好幾倍用戶體驗(yàn)也好得多。推理性能調(diào)優(yōu)時(shí)我一般會(huì)同時(shí)盯住下面幾個(gè)核心指標(biāo)延遲Latency從請求發(fā)出到拿到完整響應(yīng)的時(shí)間細(xì)分下來還包括首token時(shí)間TTFT和每token生成時(shí)間TPOT。在線業(yè)務(wù)對延遲特別敏感比如對話類場景首token超過3秒用戶基本就會(huì)覺得“卡了”。吞吐量Throughput單位時(shí)間內(nèi)能處理的請求數(shù)或token數(shù)。離線批處理場景更看重吞吐比如批量文檔解析、離線生成。GPU利用率這塊很多人會(huì)忽略。GPU利用率低往往意味著算子沒有充分利用硬件或者數(shù)據(jù)加載/預(yù)處理成了瓶頸。顯存占用模型權(quán)重、KV Cache、中間激活值、CUDA context加起來分分鐘能把顯存吃滿。并發(fā)能力服務(wù)在同一時(shí)刻能支撐的請求連接數(shù)直接決定你能扛多大的業(yè)務(wù)流量。表格整理一下方便對照場景選指標(biāo)指標(biāo)適用場景主要瓶頸來源延遲含TTFT/TPOT在線對話、實(shí)時(shí)交互模型規(guī)模、解碼方式、算子效率吞吐量離線批處理、數(shù)據(jù)生產(chǎn)batch size、并發(fā)數(shù)、顯存帶寬GPU利用率所有場景數(shù)據(jù)加載、CPU預(yù)處理、算子調(diào)度顯存占用所有場景模型權(quán)重、KV Cache、中間激活并發(fā)能力高并發(fā)API服務(wù)框架線程模型、顯存容量、顯存管理策略2.2 推理瓶頸往往不在模型本身我做過的幾個(gè)調(diào)優(yōu)案例里印象最深刻的一個(gè)是模型本身算力占用不大但接口整體延遲高得離譜。排查到最后發(fā)現(xiàn)問題出在輸入數(shù)據(jù)的預(yù)處理環(huán)節(jié)——每來一個(gè)請求服務(wù)端都要做一次圖像解碼、縮放、歸一化而這一步跑在CPU上且是串行執(zhí)行的。GPU在那閑著等數(shù)據(jù)CPU成了瓶頸整個(gè)鏈路就被拖慢了。這個(gè)案例很有代表性。推理性能調(diào)優(yōu)的第一步永遠(yuǎn)是先定位瓶頸而不是盲目上優(yōu)化手段。一般來說瓶頸可能出現(xiàn)在以下幾個(gè)位置數(shù)據(jù)預(yù)處理/后處理CPU密集操作、編解碼耗時(shí)、數(shù)據(jù)格式轉(zhuǎn)換經(jīng)常被忽視。CPU與GPU之間的數(shù)據(jù)傳輸小批量請求頻繁拷貝數(shù)據(jù)PCIe傳輸耗時(shí)占比會(huì)非常高。模型計(jì)算本身算子實(shí)現(xiàn)低效、內(nèi)存訪問模式不佳、圖優(yōu)化未開啟。推理框架與調(diào)度線程數(shù)配置不當(dāng)、顯存分配策略不合理、動(dòng)態(tài)shape引起的重編譯。服務(wù)端網(wǎng)絡(luò)與IO請求排隊(duì)、連接池耗盡、序列化/反序列化開銷。定位瓶頸有一個(gè)很樸素但有效的方法先把模型單獨(dú)拿出來做一條龍壓測逐步拆解每一段的耗時(shí)占比。比如你用profile工具看一遍時(shí)間線如果發(fā)現(xiàn)GPU kernel執(zhí)行只占了20%時(shí)間其余都在數(shù)據(jù)搬運(yùn)和CPU處理那砸再多GPU算力也白搭。2.3 用哪個(gè)指標(biāo)判斷優(yōu)化效果調(diào)優(yōu)過程中一定要先定好基準(zhǔn)。我推薦的做法是固定同一份測試數(shù)據(jù)集包含不同長度的輸入固定batch size和并發(fā)數(shù)跑足夠多輪取p50/p99延遲同時(shí)記錄吞吐量。調(diào)優(yōu)前后對比這些數(shù)據(jù)而不是看單次運(yùn)氣好的結(jié)果。我自己做調(diào)優(yōu)時(shí)第一件事是在服務(wù)里埋好日志記錄每次請求的預(yù)處理耗時(shí)、排隊(duì)耗時(shí)、GPU kernel耗時(shí)、后處理耗時(shí)。后面所有的優(yōu)化動(dòng)作都能用數(shù)據(jù)說話哪段耗時(shí)長就去優(yōu)化哪段避免憑感覺調(diào)參。3. 模型層面的優(yōu)化從權(quán)重到算子的瘦身術(shù)3.1 模型量化性價(jià)比最高的第一刀模型量化是推理性能調(diào)優(yōu)里回報(bào)率最高的手段之一。原理很簡單訓(xùn)練時(shí)模型權(quán)重通常用FP32或FP16存儲(chǔ)推理時(shí)其實(shí)沒必要保持這么高的精度把權(quán)重從FP32壓到FP16能直接減半顯存占用從FP16壓到INT8還能再減一半。代價(jià)是部分任務(wù)精度會(huì)有輕微下降。實(shí)際項(xiàng)目中大模型的KV Cache同樣會(huì)消耗大量顯存。KV Cache量化比如從FP16壓到INT8甚至INT4可以顯著提升并發(fā)能力因?yàn)橥瑯拥娘@存可以塞下更多請求的KV緩存。以我在模擬項(xiàng)目X里的一個(gè)7B模型為例權(quán)重從FP16切到INT8后單卡能處理的并發(fā)數(shù)從16提升到了28顯存占用降低了約45%。量化落地時(shí)有兩點(diǎn)值得特別注意不是所有算子都適合INT8量化。像LayerNorm、Softmax這類對精度敏感的算子通常保持高精度量化主要集中在矩陣乘法和Embedding層。量化后的模型一定要做精度驗(yàn)證。用一小部分測試集對比量化前后輸出差異超過可接受閾值就需要回退部分層或者改用混合精度方案。表不同量化精度對顯存和速度的影響7B模型實(shí)測精度方案顯存占用相對吞吐提升精度損失FP32100%基準(zhǔn)1x無FP16/BF16約50%1.6x可忽略INT8約25%2.1x低需驗(yàn)證INT4約12.5%2.8x中需謹(jǐn)慎3.2 剪枝、蒸餾與模型結(jié)構(gòu)精簡量化解決了存儲(chǔ)和計(jì)算強(qiáng)度的問題但很多場景下還需要在結(jié)構(gòu)上做減法這就是模型剪枝和蒸餾的用武之地。模型剪枝的做法是去掉神經(jīng)網(wǎng)絡(luò)中對最終輸出貢獻(xiàn)較小的權(quán)重連接或甚至整個(gè)通道。當(dāng)前很多開源模型本身就存在參數(shù)冗余剪掉一部分權(quán)重后精度下降有限但計(jì)算量會(huì)明顯減少。實(shí)際操作中結(jié)構(gòu)化剪枝比非結(jié)構(gòu)化剪枝更適合推理加速因?yàn)榉墙Y(jié)構(gòu)化剪枝產(chǎn)生的稀疏矩陣在GPU上不太好發(fā)揮除非硬件專門支持稀疏計(jì)算。模型蒸餾則是用一個(gè)大的教師模型去指導(dǎo)一個(gè)小學(xué)生模型學(xué)習(xí)讓小學(xué)生模型在參數(shù)量小很多的情況下盡量逼近教師模型的輸出。蒸餾后的模型精度可能略低但推理成本下降得非常明顯。我見過一個(gè)團(tuán)隊(duì)把BERT-base蒸餾成三層的小模型在線問答場景延遲從80ms降到了20ms效果還能接受。選擇哪種手段要看業(yè)務(wù)容忍度毫秒級延遲敏感場景優(yōu)先上蒸餾和小規(guī)模模型別硬扛大模型。離線高吞吐場景可以用量化大批次的方案不用太在意單次延遲。精度要求極高場景盡量保留高精度權(quán)重只在KV Cache和激活值上做溫和量化。3.3 算子融合與計(jì)算圖優(yōu)化模型推理時(shí)計(jì)算圖里有很多細(xì)碎的算子頻繁在GPU上啟動(dòng)內(nèi)核kernel。每個(gè)kernel啟動(dòng)都有固定開銷雖然單次只有幾十微秒但模型里有幾百上千個(gè)算子累積起來非??捎^。算子融合就是把相鄰的幾個(gè)算子合并成一個(gè)減少kernel啟動(dòng)次數(shù)同時(shí)減少中間結(jié)果的讀寫。最經(jīng)典的融合案例是把“矩陣乘法偏置激活函數(shù)”融合成一個(gè)算子。比如Transformer里的Attention計(jì)算把QKV映射、縮放、Softmax、輸出映射等步驟盡量融合能省掉不少中間顯存讀寫。PyTorch的torch.compile、TensorRT的圖優(yōu)化、ONNX Runtime的graph optimization都在做類似的事。我用torch.compile試過一個(gè)12層的Transformer模型在A100上推理延遲直接降了18%且無需改任何業(yè)務(wù)代碼。但要注意torch.compile首次運(yùn)行會(huì)有較長的編譯時(shí)間服務(wù)上線時(shí)需要預(yù)熱的機(jī)制否則第一個(gè)請求會(huì)特別慢。4. 推理引擎與服務(wù)架構(gòu)的選型邏輯4.1 主流推理引擎橫向?qū)Ρ冗x推理引擎是部署中繞不開的決策點(diǎn)。我用過的方案包括PyTorch原生、ONNX Runtime、TensorRT、以及專門的大模型推理框架如vLLM、TGI、SGLang。各有各的優(yōu)劣勢不存在萬能選擇。引擎優(yōu)勢劣勢適用場景PyTorch原生上手快、兼容好、生態(tài)全性能一般、顯存占用高原型驗(yàn)證、快速迭代ONNX Runtime跨平臺(tái)、優(yōu)化好、部署輕部分算子轉(zhuǎn)換有坑通用模型上云、端側(cè)部署TensorRTGPU上性能極致構(gòu)建時(shí)間長、硬件綁定生產(chǎn)環(huán)境、固定shape場景vLLM/TGI大模型專用、吞吐極高依賴特定GPU架構(gòu)對話、生成、大規(guī)模并發(fā)我比較推薦的生產(chǎn)路徑是先用PyTorch做功能和正確性驗(yàn)證再導(dǎo)出ONNX或直接用TensorRT做性能優(yōu)化。如果模型真的很小或者對上線速度要求極快PyTorch torch.compile也能撐一段時(shí)間但后續(xù)擴(kuò)容時(shí)大概率還得換引擎。4.2 單機(jī)多卡部署時(shí)的切分策略模型大到單卡放不下就得考慮多卡部署。常見的有張量并行、流水線并行和數(shù)據(jù)并行三者解決的問題完全不同。張量并行把一層里的矩陣運(yùn)算切分到多張卡適合單層計(jì)算量極大、顯存放不下的場景。缺點(diǎn)是卡間通信頻繁對NVLink帶寬要求很高。流水線并行把模型按層切分成多段每張卡負(fù)責(zé)若干層數(shù)據(jù)按順序流過各卡。適合層數(shù)很深的模型但會(huì)出現(xiàn)流水線氣泡利用率不一定理想。數(shù)據(jù)并行每張卡都放完整模型各自處理不同batch的數(shù)據(jù)最后同步梯度或結(jié)果。推理時(shí)基本不涉及梯度同步所以擴(kuò)展性很好。實(shí)際部署中這些策略往往組合使用。大模型推理通常先用張量并行解決單卡放不下的問題再用數(shù)據(jù)并行提升整體吞吐。通信開銷是必須重點(diǎn)關(guān)注的如果卡間通信瓶頸嚴(yán)重多卡跑的收益可能還不如單卡。我做過一個(gè)對比7B模型在單卡上能跑但并發(fā)很低切成兩卡張量并行后單次延遲反而增加了約10%因?yàn)橥ㄐ砰_銷抵消了算力收益。但對于更大規(guī)模的模型比如70B級不多卡部署根本跑不起來通信開銷屬于必須付出的代價(jià)。4.3 動(dòng)態(tài)batch與Continuous Batching傳統(tǒng)的靜態(tài)batching是等一批請求湊夠數(shù)量再統(tǒng)一推理缺點(diǎn)是等待時(shí)間不可控而且batch里長短不一的序列會(huì)互相拖累——都得按照最長序列來算浪費(fèi)嚴(yán)重。大模型推理框架普遍引入了Continuous Batching連續(xù)批處理本質(zhì)是不再要求一個(gè)batch內(nèi)的請求同進(jìn)同出。某個(gè)請求生成完一個(gè)token就退出新請求隨時(shí)可以插入GPU始終在處理有效計(jì)算。這個(gè)機(jī)制對吞吐的提升非常明顯。以vLLM為例在同等資源下相同并發(fā)壓測中吞吐往往是Naive PyTorch實(shí)現(xiàn)的數(shù)倍。部署時(shí)如果想用上Continuous Batching就別自己造輪子了直接選vLLM或TGI這類成熟框架內(nèi)部已經(jīng)實(shí)現(xiàn)了顯存管理和調(diào)度邏輯比手寫高效得多。5. 運(yùn)行時(shí)參數(shù)調(diào)優(yōu)與關(guān)鍵配置5.1 顯存管理與緩存策略顯存是推理性能最大的物理約束之一。做大模型推理時(shí)顯存主要消耗在三塊模型權(quán)重、KV Cache、臨時(shí)激活值。模型權(quán)重基本固定臨時(shí)激活值隨batch和序列長度波動(dòng)KV Cache則是優(yōu)化空間最大的部分。幾個(gè)實(shí)用的顯存優(yōu)化思路盡量復(fù)用顯存框架層面做好內(nèi)存池避免每次請求都重新分配。vLLM就是通過PagedAttention把KV Cache分塊管理大幅減少了碎片和浪費(fèi)。調(diào)整KV Cache策略將KV Cache從FP16降到INT8或INT4或者限制最大序列長度能直接提升并發(fā)承載量。開啟顯存不夠時(shí)的自動(dòng)換出部分框架支持把不活躍的請求KV Cache換到CPU內(nèi)存但換入換出會(huì)增加延遲適合吞吐優(yōu)先的離線場景。用小batch多迭代如果顯存緊張與其一次塞大batch導(dǎo)致OOM不如拆分幾個(gè)小batch控制在顯存安全線以內(nèi)。5.2 線程、并發(fā)與批大小設(shè)置服務(wù)端的線程數(shù)和并發(fā)設(shè)置也是性能調(diào)優(yōu)的關(guān)鍵。PyTorch的CPU線程數(shù)默認(rèn)可能吃滿所有核心但在GPU推理場景下CPU線程太多反而會(huì)帶來上下文切換開銷。建議按以下思路設(shè)置torch.set_num_threads如果預(yù)處理在CPU上執(zhí)行可以預(yù)留一部分CPU核給數(shù)據(jù)加載剩下的給計(jì)算線程。模型服務(wù)的max_batch_size不宜盲目設(shè)大。batch增大能提升吞吐但延遲會(huì)同步上升需要找到吞吐與延遲的平衡點(diǎn)。我一般壓測時(shí)會(huì)從1、2、4、8、16逐級往上試觀察p99延遲曲線何時(shí)開始拐彎。并發(fā)請求數(shù)壓測時(shí)并發(fā)太低可能喂不飽GPU太高又可能導(dǎo)致排隊(duì)加劇??梢杂脡簻y工具逐步加壓監(jiān)控GPU利用率和響應(yīng)時(shí)間找到拐點(diǎn)。有一個(gè)容易踩的坑動(dòng)態(tài)shape會(huì)導(dǎo)致推理框架在運(yùn)行時(shí)不斷重新編譯或layout變換性能嚴(yán)重劣化。我的做法是能固定shape就固定不能固定就限制為幾個(gè)離散檔位如長度128/256/512/1024框架內(nèi)做padding到檔位長度。雖然會(huì)浪費(fèi)一點(diǎn)算力但換來的穩(wěn)定性非常值。5.3 預(yù)熱、模型編譯與大模型特有參數(shù)部署服務(wù)上線的第一分鐘往往性能很差。原因是模型首次推理時(shí)需要加載權(quán)重、建立cuDNN/tensorRT的benchmark、編譯計(jì)算圖。生產(chǎn)環(huán)境一定要做預(yù)熱具體做法是服務(wù)啟動(dòng)后用幾條具有代表性的數(shù)據(jù)跑幾輪推理把編譯和緩存預(yù)熱到位再開始對外提供流量。大模型推理框架里幾個(gè)關(guān)鍵參數(shù)值得重點(diǎn)調(diào)gpu_memory_utilization控制在0.85-0.9之間比較穩(wěn)妥太低浪費(fèi)顯存太高容易OOM遇到碎片化嚴(yán)重時(shí)還需要降一點(diǎn)。max_num_seqs / max_num_batched_tokens決定并發(fā)上限需要根據(jù)業(yè)務(wù)峰值設(shè)置。設(shè)太大會(huì)導(dǎo)致顯存瞬間爆掉太小則浪費(fèi)算力。enable_prefix_caching開啟后相同的請求前綴比如系統(tǒng)提示詞的KV Cache可以復(fù)用多輪對話場景收益明顯。streaming輸出對話場景優(yōu)先使用流式輸出用戶感知到的首token延遲會(huì)大幅降低。6. 一次完整的推理性能調(diào)優(yōu)案例記錄6.1 壓測準(zhǔn)備與基線數(shù)據(jù)我在模擬項(xiàng)目X中部署過一個(gè)規(guī)模中等的對話模型單卡初始配置用的是PyTorch原生服務(wù)FP16權(quán)重動(dòng)態(tài)batch關(guān)閉最大并發(fā)16。壓測用的是一組平均輸入長度512token、輸出長度128token的請求共500條?;€數(shù)據(jù)如下指標(biāo)數(shù)值平均首token延遲820ms平均總延遲6.4s吞吐量2.8 req/sGPU峰值利用率47%顯存峰值占用15.2GB看到這個(gè)數(shù)據(jù)第一個(gè)直覺就是GPU利用率太低了只有47%說明大量時(shí)間在等待數(shù)據(jù)或者算子沒吃滿。接下來開始逐項(xiàng)優(yōu)化。6.2 逐步優(yōu)化過程與效果第一刀先換推理引擎。我把服務(wù)從PyTorch原生換成了vLLM啟用Continuous Batching和PagedAttention。僅僅這一步吞吐就從2.8 req/s提升到了5.6 req/s翻了一倍。GPU利用率也升到了72%。原因是動(dòng)態(tài)batch和顯存管理帶來的收益非常直觀。第二刀模型量化。把權(quán)重從FP16量化到INT8KV Cache保持FP16。量化后模型吞吐進(jìn)一步從5.6提升到7.1 req/s顯存峰值降到了約11.8GB。513條測試數(shù)據(jù)里輸出質(zhì)量對比差異基本看不出來。第三刀優(yōu)化服務(wù)端參數(shù)。把max_num_seqs從16調(diào)高到32gpu_memory_utilization設(shè)為0.88同時(shí)開啟prefix caching。吞吐提升到8.3 req/sp99總延遲穩(wěn)在7.8s以內(nèi)。這個(gè)時(shí)候GPU利用率已經(jīng)達(dá)到86%顯存還有約1.5GB余量。第四刀把輸入長度限制為128的倍數(shù)并關(guān)閉動(dòng)態(tài)shape。這個(gè)操作對單次推理延遲的降低有一定幫助首token延遲從平均441ms降到了382ms。最終優(yōu)化后數(shù)據(jù)指標(biāo)優(yōu)化前優(yōu)化后平均首token延遲820ms382ms平均總延遲6.4s4.1s吞吐量2.8 req/s8.3 req/sGPU峰值利用率47%86%顯存峰值占用15.2GB11.8GB整體吞吐提升了接近3倍延遲下降了36%。整個(gè)過程沒有改一行模型代碼完全通過引擎選型、量化、參數(shù)調(diào)優(yōu)實(shí)現(xiàn)。這也再次驗(yàn)證了那個(gè)觀點(diǎn)很多性能問題壓根不是模型本身的問題而是部署方案沒選對、配置沒調(diào)好。6.3 多場景下的部署建議匯總根據(jù)不同的業(yè)務(wù)場景我建議的部署策略也不同整理成一個(gè)速查表場景推薦方案關(guān)鍵配置在線對話/交互vLLM/TGI INT8量化開啟streaming、prefix caching大規(guī)模離線生產(chǎn)TensorRT 固定shape大batch、高吞吐優(yōu)先端側(cè)/邊緣部署ONNX Runtime 量化壓縮體積、CPU多線程優(yōu)化高精度要求場景FP16/BF16 TensorRT不激進(jìn)量化只做圖優(yōu)化小模型快速上線PyTorch torch.compile預(yù)熱、限制并發(fā)、避免動(dòng)態(tài)shape需要注意任何時(shí)候都不建議直接上線不經(jīng)過壓測的服務(wù)。即使方案看起來合理也必須在小流量下壓一遍確認(rèn)真實(shí)數(shù)據(jù)而不是紙面推算。7. 常見問題與排查技巧實(shí)錄7.1 GPU利用率低總感覺性能沒榨干現(xiàn)象GPU利用率只有30%-50%延遲卻不低。優(yōu)先排查以下環(huán)節(jié)數(shù)據(jù)加載和預(yù)處理是不是在CPU上串行執(zhí)行如果是試試異步數(shù)據(jù)加載、多進(jìn)程預(yù)處理或者把預(yù)處理算子放到GPU上。請求batch是不是太小小batch喂不飽GPU吞吐自然上不去。是不是存在動(dòng)態(tài)shape導(dǎo)致的recompile觀察第一次請求后有沒有周期性卡頓有的話就固定shape。是不是CPU線程數(shù)設(shè)置不合理導(dǎo)致線程切換開銷過大。排查工具上我慣用nvidia-smi看實(shí)時(shí)利用率配合Nsight Systems看完整時(shí)間線能非常清楚地看到每個(gè)環(huán)節(jié)的耗時(shí)占比。如果沒有這類工具也可以用簡單的計(jì)時(shí)埋點(diǎn)把預(yù)處理、傳輸、推理、后處理每一段時(shí)間打印出來。7.2 顯存OOM與溢出問題顯存OOM是部署中最常見的故障之一。OOM不一定代表顯存總量不夠更多時(shí)候是分配策略或者某個(gè)突發(fā)峰值導(dǎo)致。排查思路先分清是權(quán)重放不下還是運(yùn)行時(shí)峰值超了。如果是權(quán)重放不下直接量化權(quán)重或者換多卡部署。如果是運(yùn)行期峰值超了調(diào)整batch大小、限制max_num_seqs、降低gpu_memory_utilization、縮小或量化KV Cache。還有一種情況是顯存碎片化服務(wù)跑了一段時(shí)間后OOM但重啟就好。這是因?yàn)椴粩喾峙浜歪尫挪煌笮〉娘@存塊碎片越來越多。vLLM這類框架的PagedAttention能大幅緩解碎片問題用PyTorch原生的話可以考慮顯存池或定期重啟服務(wù)的土辦法不優(yōu)雅但管用。7.3 首token延遲高但整體吞吐正常如果首token延遲特別高多半卡在prefill階段也就是處理輸入Prompt階段。大模型解碼分兩段prefill把整段輸入一次性算完generate階段才逐token生成。輸入越長prefill耗時(shí)越高。優(yōu)化手段包括讓模型支持并行prefill某些框架默認(rèn)支持??s短輸入的無效內(nèi)容從業(yè)務(wù)層做輸入裁剪。用chunked prefill機(jī)制把超長輸入拆成小塊逐步計(jì)算避免一次性算完導(dǎo)致延遲尖刺。開啟prefix caching多輪對話和共用system prompt時(shí)效果明顯。調(diào)低并發(fā)減少排隊(duì)等待時(shí)間。7.4 并發(fā)高時(shí)延遲快速劣化并發(fā)一高延遲就起飛通常有兩個(gè)原因一是請求排隊(duì)時(shí)間過長二是顯存不夠?qū)е翶V Cache被換出。應(yīng)對方法優(yōu)先保證p99延遲在容忍范圍內(nèi)適當(dāng)通過限流控制并發(fā)數(shù)量。開連續(xù)批處理避免靜態(tài)batch帶來的等待效應(yīng)。增加服務(wù)實(shí)例利用水平擴(kuò)展分散壓力。如果單機(jī)已經(jīng)是極限就需要考慮多卡或分布式部署了。8. 最后的實(shí)操心得分享幾個(gè)我在多次上線部署中學(xué)到的經(jīng)驗(yàn)不算什么高深理論但真的能救命第一性能調(diào)優(yōu)一定要以監(jiān)控?cái)?shù)據(jù)為驅(qū)動(dòng)。不要看感覺不要憑經(jīng)驗(yàn)猜。每個(gè)優(yōu)化動(dòng)作前后都要有明確的指標(biāo)對比這樣出了問題才能快速回滾也不會(huì)白忙一場。第二不要把所有雞蛋放在一個(gè)籃子里。推理引擎、量化方案、服務(wù)架構(gòu)這些組件盡量做成可切換的。我見過一些團(tuán)隊(duì)把TensorRT優(yōu)化后的模型寫死進(jìn)代碼里后來模型迭代了TensorRT重新構(gòu)建要花好幾個(gè)小時(shí)上線節(jié)奏被拖得很慘。合理做法是把優(yōu)化鏈路自動(dòng)化模型一變構(gòu)建流程自動(dòng)跑一遍產(chǎn)物自動(dòng)驗(yàn)證。第三注意壓測數(shù)據(jù)的代表性。很多人拿固定長度的輸入做壓測看起來數(shù)據(jù)很漂亮一上真實(shí)業(yè)務(wù)就被長短不一的輸入打回原形。建議壓測數(shù)據(jù)的長度分布盡量貼近線上真實(shí)請求否則調(diào)出來的參數(shù)很容易在真實(shí)場景翻車。第四小步快跑一次只改一個(gè)變量。同時(shí)換引擎、開量化、改并發(fā)配置如果性能有波動(dòng)根本分不清是哪一步引起的。我習(xí)慣一次只調(diào)整一項(xiàng)跑一輪壓測記錄數(shù)據(jù)然后再動(dòng)下一項(xiàng)。調(diào)優(yōu)過程會(huì)慢一些但每步結(jié)論都很扎實(shí)。推理性能調(diào)優(yōu)是一個(gè)系統(tǒng)工程從模型結(jié)構(gòu)化簡、量化、算子融合到推理引擎選型、服務(wù)架構(gòu)設(shè)計(jì)、運(yùn)行時(shí)參數(shù)配置每一環(huán)都有文章可做。這篇文章算是我近期實(shí)操的一份沉淀也希望能幫你少走一些彎路。如果你手頭正好有模型要部署不妨先從基線測試開始一步步來。