戰(zhàn):從推理卡定位到性能優(yōu)化)
前一陣把一批YOLO目標(biāo)檢測(cè)任務(wù)從GPU遷移到Atlas 300V 24G加速卡上整個(gè)過(guò)程比預(yù)想的曲折但也因此把這張卡的脾氣摸了個(gè)透。我發(fā)現(xiàn)atlas部署yolo、atlas 300v 24g 是運(yùn)算加速卡嗎這兩個(gè)問(wèn)題幾乎是所有新接觸這塊卡的人繞不開(kāi)的困惑。這篇文章不打算寫(xiě)成產(chǎn)品手冊(cè)而是把這張卡到底算什么、YOLO怎么在上面真正跑起來(lái)、實(shí)測(cè)中會(huì)遇到哪些坎三條線(xiàn)串在一起講清楚給手里已經(jīng)拿到卡、或者正在選型的朋友一套可以直接參考的判斷路徑。先說(shuō)結(jié)論Atlas 300V 24G不是傳統(tǒng)意義上那種運(yùn)算加速卡它是昇騰生態(tài)里專(zhuān)門(mén)為AI推理場(chǎng)景設(shè)計(jì)的推理加速卡。這個(gè)定位差異決定了后面一系列部署方式的不同——你不能像用顯卡那樣直接跑PyTorch源碼也不能指望它兼容CUDA生態(tài)但它也有通用計(jì)算卡比不了的優(yōu)勢(shì)低功耗、高能效比、單卡大顯存以及為卷積和推理場(chǎng)景做了深度優(yōu)化的底層架構(gòu)。下面從硬件定位開(kāi)始一步一步拆解。1. Atlas 300V 24G算什么卡AI推理卡和通用計(jì)算卡的邊界1.1 運(yùn)算加速卡這個(gè)叫法其實(shí)容易誤導(dǎo)人很多人拿到Atlas 300V 24G第一反應(yīng)是把它和手上正在用的顯卡對(duì)比甚至有人問(wèn)能不能跑CUDA代碼。這里必須先劃清邊界Atlas系列不是GPU它內(nèi)部的核心不是CUDA Core而是昇騰自研的達(dá)芬奇架構(gòu)AI Core。它確實(shí)能做大量并行計(jì)算但設(shè)計(jì)目標(biāo)不是通用的并行計(jì)算平臺(tái)而是把訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)模型高效地跑到線(xiàn)上。更準(zhǔn)確地說(shuō)Atlas 300V 24G是一張AI推理加速卡。它的工作節(jié)奏是模型已經(jīng)訓(xùn)練好參數(shù)已經(jīng)固定你要做的是把每張輸入圖片在毫秒級(jí)時(shí)間內(nèi)完成前向計(jì)算輸出檢測(cè)框、分類(lèi)結(jié)果或者關(guān)鍵點(diǎn)坐標(biāo)。這個(gè)場(chǎng)景下模型的算子類(lèi)型相對(duì)固定計(jì)算圖結(jié)構(gòu)不變完全可以做深度編譯優(yōu)化把算子調(diào)度、內(nèi)存分配、數(shù)據(jù)搬運(yùn)都提前規(guī)劃好。這也是它和通用計(jì)算卡最本質(zhì)的區(qū)別——一個(gè)是什么都能算一個(gè)是把AI推理這件事做到極致。1.2 達(dá)芬奇架構(gòu)和GPU的底層差異昇騰310P采用達(dá)芬奇架構(gòu)核心計(jì)算單元是AI Core。AI Core內(nèi)部有三個(gè)關(guān)鍵部分Cube單元負(fù)責(zé)矩陣計(jì)算Vector單元負(fù)責(zé)向量運(yùn)算Scalar單元負(fù)責(zé)標(biāo)量計(jì)算。這種異構(gòu)設(shè)計(jì)的好處是卷積、全連接這類(lèi)高密度矩陣乘運(yùn)算可以扔給Cube單元批量歸一化、激活函數(shù)這類(lèi)逐元素操作交給Vector單元控制邏輯和標(biāo)量運(yùn)算則由Scalar單元兜底。和GPU的SIMT單指令多線(xiàn)程模型相比達(dá)芬奇架構(gòu)更接近專(zhuān)用加速器的思路。它的調(diào)度粒度更粗、計(jì)算模式更固定好處是能效比高——一塊Atlas 300V 24G的整卡功耗大約在72W到75W但I(xiàn)NT8算力可以做到140 TOPS級(jí)別。相比之下一塊能跑到140 TOPS INT8的GPU顯卡功耗通常要高出一大截。代價(jià)也很明顯它不跑CUDA、不跑OpenCL生態(tài)工具鏈和GPU是兩套體系所有模型都必須經(jīng)過(guò)專(zhuān)門(mén)的編譯轉(zhuǎn)換才能運(yùn)行。1.3 Atlas產(chǎn)品線(xiàn)和300V的定位昇騰Atlas產(chǎn)品家族線(xiàn)上線(xiàn)下被反復(fù)提及很多人在選型時(shí)就先懵了。我簡(jiǎn)單梳理一下Atlas 200是嵌入式模組適合機(jī)器人、邊緣小盒子Atlas 300I是標(biāo)準(zhǔn)推理卡常用于數(shù)據(jù)中心AI推理服務(wù)器Atlas 300V系列則主打視頻解析和智能視覺(jué)場(chǎng)景300V Pro型號(hào)提供24GB大顯存適合多路視頻流、高分辨率圖像檢測(cè)這類(lèi)顯存消耗大的任務(wù)。此外還有Atlas 800推理服務(wù)器這類(lèi)整機(jī)產(chǎn)品。所以如果你手里拿的是Atlas 300V 24G基本可以判斷它的定位就是視覺(jué)推理卡YOLO目標(biāo)檢測(cè)恰好是它的主軸場(chǎng)景。搞清楚這個(gè)定位之后后續(xù)所有部署決策都會(huì)順理成章不需要考慮訓(xùn)練場(chǎng)景不需要考慮通用計(jì)算場(chǎng)景專(zhuān)注把推理鏈路打通、把吞吐和延遲優(yōu)化到位就行。2. 影響部署方式的硬件參數(shù)顯存、算力、帶寬逐個(gè)過(guò)一遍2.1 24G顯存到底能裝下什么Atlas 300V Pro 24G搭載24GB LPDDR4X顯存。這個(gè)容量放在AI推理卡里算很扎實(shí)了。以YOLOv5s為例FP16精度下模型權(quán)重大約28MB單張640x640輸入圖的中間特征圖占用也就在幾十MB量級(jí)光看單模型的話(huà)24G顯存完全用不滿(mǎn)。但實(shí)際跑業(yè)務(wù)時(shí)顯存消耗大頭往往不是模型本身而是并發(fā)路數(shù)和圖像分辨率。如果你要處理1080P甚至4K分辨率輸入特征圖尺寸會(huì)成倍擴(kuò)大如果你用多路視頻流并發(fā)推理每路都要獨(dú)立的前處理緩沖和輸出緩沖再加上你可能會(huì)在一個(gè)卡上加載多個(gè)模型這些疊加起來(lái)8G顯存的卡就會(huì)很緊張。24G版本的核心價(jià)值就在這里——它能讓你在高分辨率多路并發(fā)多模型加載的組合場(chǎng)景下不必精打細(xì)算開(kāi)發(fā)省心很多。拿我實(shí)際測(cè)試來(lái)說(shuō)加載一個(gè)進(jìn)過(guò)INT8量化的YOLOv5s模型顯存占用大約在500MB到1GB之間具體取決于AIPP配置和輸入輸出緩沖區(qū)大小。剩下的空間足夠再做多batch推理和緩沖池設(shè)計(jì)。2.2 INT8 140 TOPS的算力口徑要看清Atlas 300V 24G標(biāo)稱(chēng)INT8算力約為140 TOPS很多人看到這個(gè)數(shù)字會(huì)覺(jué)得碾壓顯卡實(shí)際要冷靜。TOPS是理論峰值只在算子的計(jì)算密度足夠高、數(shù)據(jù)搬運(yùn)不成為瓶頸時(shí)才能接近。真實(shí)推理場(chǎng)景里YOLO模型包含大量小算子、逐元素操作和數(shù)據(jù)的搬運(yùn)AI Core很難全程跑滿(mǎn)。另外要注意精度口徑140 TOPS是INT8的理論值FP16精度下算力會(huì)下降不少FP32更低。所以部署YOLO時(shí)如果對(duì)精度損失可以接受通常建議做INT8量化這樣才能真正發(fā)揮這張卡的算力優(yōu)勢(shì)如果必須保持FP16實(shí)際能榨出來(lái)的性能比INT8要打?qū)φ凵踔粮噙@點(diǎn)在算力規(guī)劃時(shí)就要提前想清楚。2.3 PCIe、供電和物理安裝的兼容問(wèn)題Atlas 300V 24G采用標(biāo)準(zhǔn)PCIe 3.0 x16接口半高半長(zhǎng)雙寬形態(tài)。這意味著普通的x86服務(wù)器都能插不挑主板也不需要外接輔助供電單卡功耗在72W左右從PCIe插槽取電就夠。但物理安裝時(shí)有兩個(gè)細(xì)節(jié)容易忽略。一是雙寬——卡體厚度占用兩個(gè)槽位服務(wù)器機(jī)箱如果槽位排布緊密旁邊的擴(kuò)展卡可能裝不下。二是散熱這張卡是被動(dòng)散熱設(shè)計(jì)依靠服務(wù)器風(fēng)道散熱。如果裝進(jìn)普通塔式機(jī)箱風(fēng)道不好跑高負(fù)載時(shí)溫度容易飆到90度以上觸發(fā)降頻。我用的服務(wù)器是2U機(jī)箱前置風(fēng)扇組直吹PCIe區(qū)域溫度和穩(wěn)定性都還正常。2.4 軟件棧版本對(duì)應(yīng)關(guān)系硬件參數(shù)之外軟件棧版本是部署最容易亂的地方。Atlas卡的軟件生態(tài)核心是CANN昇騰計(jì)算語(yǔ)言它提供驅(qū)動(dòng)、固件、運(yùn)行時(shí)庫(kù)、算子庫(kù)和模型轉(zhuǎn)換工具。CANN版本直接決定你支持哪些算子、哪些PyTorch/ONNX版本可以轉(zhuǎn)換。經(jīng)過(guò)手頭多次測(cè)試比較穩(wěn)的組合是CANN 6.x系列 Python 3.7/3.8/3.9任一版本 較新的PyTorch 1.11/1.12或者2.0系列模型先導(dǎo)出ONNX再用ATC工具轉(zhuǎn)成OM。注意不同CANN版本對(duì)應(yīng)不同的芯片支持列表轉(zhuǎn)換前務(wù)必在官方文檔確認(rèn)--soc_version參數(shù)。310P芯片在不同產(chǎn)品上后綴不同比如Ascend310P3填錯(cuò)了轉(zhuǎn)換會(huì)直接報(bào)錯(cuò)這一項(xiàng)我踩過(guò)不止一次。3. 部署YOLO的關(guān)鍵一步PyTorch模型怎么變成OM模型3.1 為什么不能直接跑PyTorch源碼這是新手問(wèn)得最多的問(wèn)題。Atlas卡不像顯卡那樣有CUDA生態(tài)PyTorch沒(méi)法直接調(diào)用Atlas做張量計(jì)算。雖然昇騰有torch_npu適配層可以讓部分PyTorch算子跑到昇騰上但在實(shí)際項(xiàng)目中最穩(wěn)、最通用的路徑是PyTorch訓(xùn)練好的模型先導(dǎo)出成ONNX再用CANN自帶的ATCAscend Tensor Compiler工具把ONNX轉(zhuǎn)成OM昇騰原生模型格式最后基于AscendCL接口做推理。為什么不推薦直接依賴(lài)torch_npu因?yàn)榧嫒菪院托阅苷{(diào)優(yōu)成本不可控。YOLO系列模型更新快不同版本的算子實(shí)現(xiàn)差異大torch_npu適配不一定覆蓋到位。ONNX導(dǎo)出然后ATC轉(zhuǎn)換的路徑算子支持和優(yōu)化手段更透明后續(xù)部署也更可控。3.2 ONNX導(dǎo)出時(shí)的算子細(xì)節(jié)以YOLOv5s為例如果直接用ultralytics提供的export腳本導(dǎo)出ONNX大概率會(huì)遇到幾個(gè)坑。第一個(gè)是opset版本。ONNX opset版本太低某些算子沒(méi)有對(duì)應(yīng)版本實(shí)現(xiàn)版本太高ATC可能還沒(méi)跟上。實(shí)測(cè)下來(lái)opset 11到opset 13之間比較穩(wěn)妥。超過(guò)opset 17時(shí)部分ATC版本會(huì)出現(xiàn)不支持的算子報(bào)錯(cuò)。第二個(gè)是模型里的上采樣算子。 YOLOv5和YOLOv8都用到了上采樣PyTorch導(dǎo)出ONNX時(shí)默認(rèn)行為在某些版本下會(huì)導(dǎo)出為Resize算子而Resize的坐標(biāo)變換模式如果和ATC實(shí)現(xiàn)不匹配轉(zhuǎn)換就會(huì)卡住。解決辦法是導(dǎo)出時(shí)把opset_version固定在一個(gè)穩(wěn)定版本并且如果報(bào)Resize相關(guān)錯(cuò)誤把Pytorch上采樣實(shí)現(xiàn)換成固定模式。第三個(gè)是內(nèi)置NMS。很多YOLO改進(jìn)版本會(huì)在模型里封裝NMS后處理直接導(dǎo)出ONNX會(huì)得到包含NMS的完整圖。這種圖在GPU上跑沒(méi)問(wèn)題但到了ATC轉(zhuǎn)換時(shí)NMS包含了大量動(dòng)態(tài)控制流和循環(huán)邏輯AI Core對(duì)這種算子非常不友好要么轉(zhuǎn)不過(guò)去要么轉(zhuǎn)過(guò)去性能極差。所以導(dǎo)出ONNX時(shí)建議把后處理全部去掉只保留主干和檢測(cè)頭輸出。3.3 ATC轉(zhuǎn)換命令和AIPP配置導(dǎo)出一個(gè)干凈的ONNX之后就可以用ATC工具轉(zhuǎn)換OM了?;久钊缦耡tc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp.config \ --logerror這里幾個(gè)參數(shù)要特別說(shuō)明。framework5表示輸入的是ONNX模型soc_version必須和你的芯片完全匹配可以通過(guò)npu-smi info或CANN的查詢(xún)工具確認(rèn)input_shape指定了模型輸入張量的形狀因?yàn)槭庆o態(tài)shape一旦定下來(lái)后面推理就只接受這個(gè)shape的輸入insert_op_conf指向AIPP預(yù)處理配置文件。AIPP是昇騰的圖處理單元可以在硬件層面完成圖像的縮放、減均值、除以標(biāo)準(zhǔn)差、RGB/BGR格式轉(zhuǎn)換等預(yù)處理。也就是說(shuō)模型輸入直接就是歸一化后的張量不用在CPU上做這些操作。我的AIPP配置如下{ aipp_op: { aipp_mode: static, input_format: RGB, src_image_size_w: 640, src_image_size_h: 640, crop: false, mean: [0, 0, 0], min: [0, 0, 0], var: [0.003921568627451, 0.003921568627451, 0.003921568627451] } }這個(gè)配置的含義是把輸入圖片統(tǒng)一縮放為640x640格式為RGB均值設(shè)為0方差設(shè)置為1/2550.003921相當(dāng)于在硬件里完成了歸一化。這樣上層應(yīng)用只需要把BGR圖片轉(zhuǎn)成RGB、resize到640x640后續(xù)的歸一化交給AIPP處理既能降低CPU壓力也能減少一次數(shù)據(jù)搬運(yùn)。需要提醒的是PyTorch訓(xùn)練時(shí)采用的分辨率和歸一化方式必須和AIPP配置對(duì)齊否則檢測(cè)精度會(huì)明顯下降。我之前遇到過(guò)換用AIPP預(yù)處理后mAP掉了一截的情況檢查發(fā)現(xiàn)是訓(xùn)練時(shí)又做了一次均值方差歸一化導(dǎo)致數(shù)值分布完全不一致。3.4 動(dòng)態(tài)batch和動(dòng)態(tài)shape的限制ATC工具對(duì)動(dòng)態(tài)shape的支持一直是個(gè)痛點(diǎn)。標(biāo)準(zhǔn)做法是靜態(tài)shape也就是在轉(zhuǎn)換時(shí)就把batch、高度、寬度全部定死。如果你需要支持不同的batch大小有幾種做法轉(zhuǎn)多個(gè)不同batch的OM文件比如分別轉(zhuǎn)batch1、batch4、batch8三個(gè)模型文件運(yùn)行時(shí)按實(shí)際需求加載對(duì)應(yīng)模型。利用昇騰的多batch動(dòng)態(tài)能力ATC支持--dynamic_batch參數(shù)可以指定候選batch集合運(yùn)行時(shí)在這些候選值中選擇。這個(gè)機(jī)制比靜態(tài)多模型靈活內(nèi)存管理也更高效。用--dynamic_dims支持多組高寬組合。但需要注意動(dòng)態(tài)shape會(huì)降低算子融合和內(nèi)存規(guī)劃的優(yōu)化空間性能不如完全靜態(tài)的模型。我的建議是業(yè)務(wù)初期用靜態(tài)batch1先把邏輯跑通穩(wěn)定性確認(rèn)后再?lài)L試多batch動(dòng)態(tài)優(yōu)化。不要一上來(lái)就上動(dòng)態(tài)shape否則性能和Debug難度都會(huì)讓你很頭疼。4. 推理應(yīng)用搭建AscendCL調(diào)用邏輯與前后處理拆分4.1 為什么NMS要拆出來(lái)我在3.2提到了導(dǎo)出ONNX時(shí)要去掉NMS這里展開(kāi)講講為什么。從算力特性來(lái)說(shuō)AI Core擅長(zhǎng)的是密集的矩陣運(yùn)算像卷積、全連接這種大量數(shù)據(jù)做同樣計(jì)算的任務(wù)但NMS是典型的動(dòng)態(tài)邏輯它要按置信度排序、按IoU閾值判斷、循環(huán)剔除重復(fù)框每一步的循環(huán)次數(shù)都取決于前一步的結(jié)果行程完全不可預(yù)測(cè)。這種控制流密集的任務(wù)扔給AI Core硬件利用率很低轉(zhuǎn)換時(shí)還可能直接報(bào)算子不支持。所以標(biāo)準(zhǔn)做法是模型只負(fù)責(zé)輸出原始檢測(cè)頭結(jié)果通常是多個(gè)尺度的預(yù)測(cè)特征解碼、閾值過(guò)濾、NMS這些后處理全部放到CPU上執(zhí)行。YOLOv5s在640x640輸入下檢測(cè)頭輸出的原始預(yù)測(cè)約25200個(gè)候選框CPU上做NMS也就是幾毫秒的事不會(huì)成為瓶頸。這個(gè)拆分方案是昇騰社區(qū)和實(shí)際項(xiàng)目里最主流的做法。4.2 一次推理的完整調(diào)用流程基于AscendCL的推理程序標(biāo)準(zhǔn)調(diào)用序列大概是這樣的aclInit(nullptr); // 初始化CL環(huán)境 aclrtSetDevice(0); // 指定設(shè)備0為第一張Atlas卡 aclrtCreateContext(context, 0); // 創(chuàng)建上下文 aclmdlLoadFromFile(yolov5s_bs1.om, modelId); // 加載OM模型 aclmdlCreateDesc(modelDesc); aclmdlGetDesc(modelDesc, modelId); // 獲取模型描述信息 aclmdlCreateDataset(inputDataset); // 創(chuàng)建輸入數(shù)據(jù)集 // 分配輸入輸出內(nèi)存拷貝預(yù)處理后的圖像數(shù)據(jù)到輸入內(nèi)存 aclmdlExecute(modelId, inputDataset, outputDataset); // 執(zhí)行推理 // 從輸出內(nèi)存解析檢測(cè)結(jié)果做后處理 aclmdlUnload(modelId); // 卸載模型這個(gè)流程很長(zhǎng)時(shí)間都很穩(wěn)定。實(shí)際工程里要注意的細(xì)節(jié)第一輸入輸出內(nèi)存建議用aclrtMalloc申請(qǐng)?jiān)O(shè)備側(cè)內(nèi)存而不是普通的malloc因?yàn)樵O(shè)備內(nèi)存的地址對(duì)齊和訪(fǎng)問(wèn)效率更好。第二輸入數(shù)據(jù)從CPU拷貝到設(shè)備側(cè)用aclrtMemcpy這個(gè)拷貝過(guò)程如果頻繁發(fā)生會(huì)占用不少時(shí)間所以盡量用緩沖池復(fù)用內(nèi)存避免每次推理都重新分配。第三多線(xiàn)程下要特別注意Context的線(xiàn)程綁定AscendCL要求在一個(gè)Context下執(zhí)行的線(xiàn)程自己管理好同步多個(gè)線(xiàn)程往同一個(gè)模型請(qǐng)求推理時(shí)建議自己用消息隊(duì)列把請(qǐng)求排好序或者直接用昇騰提供的多路并發(fā)接口。4.3 前處理數(shù)據(jù)格式RGB/BGR和NHWC/NCHW前處理細(xì)節(jié)直接決定推理能不能跑對(duì)。PyTorch訓(xùn)練YOLO時(shí)通常輸入是NCHW格式的張量通道順序是RGB數(shù)值歸一化到0~1。但從攝像頭或視頻流解碼出來(lái)的原始幀通常都是BGR順序也有人習(xí)慣用OpenCV讀圖后直接送到網(wǎng)絡(luò)如果沒(méi)轉(zhuǎn)換就會(huì)出現(xiàn)通道錯(cuò)亂檢測(cè)結(jié)果完全不準(zhǔn)。AIPP配置里我寫(xiě)了input_format: RGB就意味著硬件默認(rèn)輸入張量是RGB順序。如果你的應(yīng)用拿到的圖像是BGR要么在上層用OpenCV的cvtColor先轉(zhuǎn)RGB要么調(diào)整AIPP配置為BGR然后在模型轉(zhuǎn)換時(shí)注意訓(xùn)練時(shí)用的通道順序。另一個(gè)常見(jiàn)問(wèn)題是數(shù)據(jù)排布ONNX模型默認(rèn)輸入是NCHW也就是batch, channel, height, width但許多推理框架習(xí)慣用NHWC這個(gè)也需要在轉(zhuǎn)換時(shí)用ATC參數(shù)保證一致。我在工程里習(xí)慣統(tǒng)一約定模型輸入定義為1,3,640,640的NCHW張量AIPP負(fù)責(zé)歸一化和RGB轉(zhuǎn)換應(yīng)用層只做resize和通道順序轉(zhuǎn)換。這樣各層職責(zé)清晰排查問(wèn)題也方便。5. 實(shí)測(cè)數(shù)據(jù)與性能優(yōu)化別被單次推理速度誤導(dǎo)5.1 單次延遲和吞吐要分開(kāi)統(tǒng)計(jì)部署完第一版推理程序后我先做了單張圖片的延遲測(cè)試。YOLOv5s在640x640輸入、batch1、FP16模型下單次推理延遲大約在8到12毫秒換算過(guò)來(lái)大概80到120FPS。這個(gè)數(shù)字看起來(lái)不錯(cuò)但真正跑業(yè)務(wù)時(shí)不能只看這個(gè)。原因在于單次推理延遲只代表模型計(jì)算時(shí)間實(shí)際業(yè)務(wù)還要加上圖像解碼、resize、通道轉(zhuǎn)換、數(shù)據(jù)拷貝到設(shè)備、設(shè)備執(zhí)行、結(jié)果拷回、后處理NMS這些環(huán)節(jié)。如果每個(gè)環(huán)節(jié)都很隨意整體耗時(shí)可能翻一倍還多。我曾在未做任何優(yōu)化的狀態(tài)下測(cè)過(guò)端到端延遲發(fā)現(xiàn)單幀總耗時(shí)超過(guò)30毫秒刨掉模型計(jì)算的10毫秒其余20毫秒全消耗在前后處理和內(nèi)存拷貝上。所以性能優(yōu)化第一步是先把端到端拆成細(xì)粒度階段用profiling工具測(cè)量每段耗時(shí)找出真正的瓶頸。5.2 batch和多批并發(fā)怎樣取舍單batch推理延遲雖然低但單張卡的算力并沒(méi)有吃滿(mǎn)。把batch加大比如從1調(diào)到4或8每次推理處理多張圖算力利用率會(huì)明顯提升。實(shí)測(cè)中YOLOv5s在batch4時(shí)總延遲大約是單batch的2到2.5倍但吞吐可以提升到單batch的1.6到1.8倍。所以如果業(yè)務(wù)是視頻流并發(fā)場(chǎng)景優(yōu)先用batch推理聚合請(qǐng)求如果是單路實(shí)時(shí)檢測(cè)場(chǎng)景那就更看重低延遲batch1反而更合適。多路并發(fā)是另一個(gè)常用手段。如果卡上有足夠顯存可以加載兩份模型實(shí)例分別處理不同的視頻流配合昇騰的硬件隊(duì)列調(diào)度能進(jìn)一步壓榨算力。但要記住雙實(shí)例意味著每個(gè)模型至少有自己獨(dú)立的推理線(xiàn)程后處理也要跟著拆分復(fù)雜度會(huì)上升。項(xiàng)目工期緊時(shí)先做batch聚合收益比雙實(shí)例大得多。5.3 AOE自動(dòng)調(diào)優(yōu)能用在哪CANN提供了AOEAscend Optimization Engine自動(dòng)調(diào)優(yōu)工具它會(huì)對(duì)模型進(jìn)行算子級(jí)和子圖級(jí)的搜索優(yōu)化嘗試不同的算子實(shí)現(xiàn)和調(diào)度策略找到更優(yōu)的配置。用法簡(jiǎn)單就是用aoe命令替代atcaoe --modelyolov5s.onnx \ --framework5 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --outputyolov5s_aoeAOE會(huì)跑一遍模型的算子搜索時(shí)間從幾分鐘到幾十分鐘不等優(yōu)化后的模型一般比直接ATC轉(zhuǎn)換出來(lái)的性能提升5%到15%。對(duì)線(xiàn)上正式環(huán)境我建議必跑一次AOE。但對(duì)快速驗(yàn)證原型直接用ATC轉(zhuǎn)出來(lái)的模型就夠了不用每次調(diào)試都等AOE跑完。6. 真實(shí)部署中遇到的坑和排查思路6.1 驅(qū)動(dòng)與固件狀態(tài)檢查跑正式業(yè)務(wù)之前第一件事永遠(yuǎn)是確認(rèn)驅(qū)動(dòng)和固件狀態(tài)正常。在服務(wù)器上執(zhí)行npu-smi info這個(gè)命令會(huì)列出當(dāng)前所有Atlas卡包括芯片溫度、功耗、顯存占用、驅(qū)動(dòng)版本和固件版本。如果顯示狀態(tài)是Fault或者Unknown先不要急著部署優(yōu)先檢查驅(qū)動(dòng)和固件版本是否匹配CANN版本。我遇到過(guò)一次卡在系統(tǒng)里能識(shí)別但加載模型時(shí)崩潰最后發(fā)現(xiàn)是固件版本太老升級(jí)固件之后恢復(fù)正常。還有一個(gè)容易忽略的點(diǎn)CANN環(huán)境變量。不同CANN版本對(duì)應(yīng)的環(huán)境變量腳本在/usr/local/Ascend/ascend-toolkit/set_env.sh不source這個(gè)文件運(yùn)行程序時(shí)經(jīng)常出現(xiàn)找不到libascendcl.so這類(lèi)動(dòng)態(tài)庫(kù)錯(cuò)誤。這不算什么高深問(wèn)題但很多人第一次跑就栽在這里。6.2 算子不支持類(lèi)報(bào)錯(cuò)的排查模型轉(zhuǎn)換階段最常見(jiàn)的報(bào)錯(cuò)就是某個(gè)算子不支持核心信息一般長(zhǎng)這樣E10001: Failed to compile op: [Gather]...遇到這種問(wèn)題先不要慌。排查路徑我一般是第一步確認(rèn)ONNX導(dǎo)出時(shí)opset版本和算子列表第二步用atc工具帶上--logdebug重新轉(zhuǎn)換看更詳細(xì)的日志定位到具體算子第三步去CANN對(duì)應(yīng)版本的算子支持列表里查這個(gè)算子是用Atlas上是否有現(xiàn)成實(shí)現(xiàn)還是需要自定義算子。對(duì)于YOLO系模型絕大多數(shù)算子都是支持的。真正高頻出現(xiàn)的不支持集中在三個(gè)地方自定義的reshape方式、某些激活函數(shù)變種、以及模型內(nèi)嵌的NMS。前兩個(gè)可以通過(guò)導(dǎo)出ONNX時(shí)做算子替換解決后一個(gè)按前面說(shuō)的拆到后處理里解決。算子不支持不是死路但要花時(shí)間去定位和改寫(xiě)。6.3 掉幀和內(nèi)存異常的常見(jiàn)原因部署到視頻流場(chǎng)景后如果發(fā)現(xiàn)掉幀或者持續(xù)運(yùn)行一段時(shí)間后內(nèi)存暴漲通常不是模型本身的問(wèn)題而是上層資源管理沒(méi)做好。最常見(jiàn)的原因有幾個(gè)推理請(qǐng)求發(fā)太快超過(guò)卡的實(shí)際處理能力導(dǎo)致任務(wù)隊(duì)列無(wú)限堆積延遲越來(lái)越大表面看起來(lái)就像掉幀。解決辦法是在輸入側(cè)做背壓控制或者使用有界隊(duì)列。每條推理線(xiàn)程都獨(dú)立申請(qǐng)了輸入輸出內(nèi)存但結(jié)束后沒(méi)有歸還緩沖池導(dǎo)致設(shè)備側(cè)內(nèi)存持續(xù)增長(zhǎng)。內(nèi)存分配和釋放的頻次太高也會(huì)帶來(lái)額外延遲。數(shù)據(jù)拷貝和推理沒(méi)有流水線(xiàn)化。單線(xiàn)程模型是拷貝→推理→后處理串行執(zhí)行如果改成雙緩沖/多緩沖即當(dāng)前幀在推理時(shí)下一幀已經(jīng)在做數(shù)據(jù)拷貝整體吞吐可以提升不少。這幾個(gè)問(wèn)題在實(shí)際工程里非常普遍。先檢查有沒(méi)有內(nèi)存泄漏其次看任務(wù)隊(duì)列長(zhǎng)度最后再用profiling工具確認(rèn)拷貝和推理是否并行基本能解決大部分異常。回到開(kāi)頭那個(gè)問(wèn)題——Atlas 300V 24G 是運(yùn)算加速卡嗎。我的理解是它是AI推理加速卡不是通用運(yùn)算加速卡。它的價(jià)值不在于什么都能算而在于把一個(gè)已經(jīng)訓(xùn)練好的模型穩(wěn)定、高效、低功耗地跑到線(xiàn)上。YOLO系列在Atlas上的部署鏈路早已成熟遇到的坑也都有規(guī)律可循。只要把模型轉(zhuǎn)換、前后端處理拆分和資源管理這三件事做好這塊卡跑起YOLO來(lái)穩(wěn)定性和性?xún)r(jià)比都相當(dāng)能打。