計實戰(zhàn):從算子映射到INT8量化落地全解析)
做硬件加速這幾年來被問得最多的一個問題就是既然 CPU 和 GPU 都能跑模型為什么還要自己折騰 AI 推理加速器一開始我也覺得這是個偽需求直到真在邊緣側(cè)設(shè)備上部署過幾個模型才明白——通用處理器要么算力不夠要么功耗壓不住真正要對功耗、時延和成本同時敏感的批量部署場景一塊精心設(shè)計的推理加速器往往比什么“通用算力神話”都靠譜。這篇就圍繞 AI 推理加速器的完整設(shè)計與優(yōu)化過程把我踩過的坑、驗證過的方案和具體實操步驟攤開講一講。這篇文章適合誰看如果你正要做 FPGA 或?qū)S眯酒系纳窠?jīng)網(wǎng)絡(luò)推理部署或者你只是想在嵌入式設(shè)備上把模型跑得更快不希望只是調(diào)庫對參數(shù)那這里面講的設(shè)計思路、算子映射方法、量化落地細節(jié)和調(diào)試經(jīng)驗應該都能直接用上。很多內(nèi)容不區(qū)分具體芯片平臺只需要理解基本的計算機組成結(jié)構(gòu)就能跟著推演一遍。1. 設(shè)計思路拆解先搞清楚到底要加速什么1.1 瓶頸在哪算力、訪存還是控制開銷推理加速器的設(shè)計第一件事不是選芯片、不是寫 RTL而是先把模型的計算畫像算清楚。我說的“計算畫像”不是看多少個浮點運算而是靜態(tài)分析每個算子到底卡在“算”還是“搬”。以卷積層為例。假設(shè)輸入特征圖是 112x112通道數(shù) 32用 64 個 3x3 卷積核、stride 2 做卷積輸出就變成 56x56x64。這層的乘累加MAC次數(shù)大約是565664323*3也就是 1.8 億次 MAC。如果目標吞吐率是 1G MAC/s那么光這一層的計算時間就是 180ms——這是純計算視角。但換個角度看內(nèi)存訪問量輸入特征圖大小是 11211232每個數(shù)假設(shè)是 INT8也就是 401KB。64 個 3x3x32 的卷積核權(quán)重一共是 18KB。輸出特征圖是 565664共 200KB。單層的片上搬運量大概 600KB 多。如果帶寬是 12.8GB/s比如常見的 DDR4 單通道搬運時間也就 47us 左右。在這個例子里把數(shù)據(jù)反復地從 DDR 搬進搬出反而遠超計算時間。這就是典型的訪存密集型算子。我自己的習慣是拿到網(wǎng)絡(luò)結(jié)構(gòu)之后先把每一層的 MAC 總數(shù)、特征圖尺寸變化、權(quán)重體積、是否有點乘或 concat 這類高訪存算子列一張表。然后粗略算一個“算術(shù)強度”計算量除以訪存量。算術(shù)強度高的層是計算受限低的屬于訪存受限。加速器設(shè)計初期起碼要做一件事優(yōu)先優(yōu)化訪存受限的算子而不是盲目堆 MAC 陣列。提示很多人一開始就興致勃勃地把卷積算子在硬件上改成最復雜的脈動陣列結(jié)果發(fā)現(xiàn)整個網(wǎng)絡(luò)里只有三成時間花在卷積上另外一半時間卡在 Pooling、Resize 和 concat 的數(shù)據(jù)搬運上這時候陣列做得再快也換不來整體性能。先分析再動手這一條原則在任何硬件項目里都適用。1.2 計算與數(shù)據(jù)流架構(gòu)選型脈動陣列、SIMD 還是混合方式明確了瓶頸下一步就是選架構(gòu)。業(yè)內(nèi)常見的推理加速器數(shù)據(jù)流方案大概可以分三類。第一類是 CPU 式的 SIMD也就是把乘加單元排成一排配合向量寄存器做單指令多數(shù)據(jù)。這種方案控制靈活可以處理各種形狀不規(guī)整的算子但需要復雜的指令調(diào)度和編譯器支持對算力利用率來說不是最極致的選擇。第二類是脈動陣列Systolic Array讓數(shù)據(jù)在陣列里規(guī)則流動一個數(shù)據(jù)能被多次復用因此訪存壓力小功耗控制得也好。缺點是控制邏輯相對固定一旦遇到不規(guī)整的結(jié)構(gòu)比如 1x1 卷積和全連接其實很規(guī)整但各種 depthwise 卷積、稀疏化的模型就很容易出現(xiàn)空閑單元利用率大打折扣。第三類就是干脆做異構(gòu)組合主體用 SIMD 或者脈動陣列跑主計算再配一塊通用的控制邏輯和 DMA 引擎專門處理數(shù)據(jù)搬運和特殊算子。這屬于工程化比較務(wù)實的方案適用范圍廣但設(shè)計復雜度也最高。我在實際項目里做過一個偏邊緣的加速器原型我們內(nèi)部叫它“模擬項目 X”最開始采用了一組 16x16 的脈動陣列跑標準卷積效果不錯但模型里一旦出現(xiàn) depthwise 卷積比如移動端網(wǎng)絡(luò)里的 DSCNN 結(jié)構(gòu)利用率和算力優(yōu)勢立刻垮掉。后來改成 8 個 SIMD 計算核配一個小規(guī)模陣列的組合方式配合專門的數(shù)據(jù)搬運指令整體吞吐反而高出將近 40%。所以架構(gòu)選型不要只看“峰值算力多高”而是要看“實際模型的結(jié)構(gòu)到底適配哪種數(shù)據(jù)流”。如果是網(wǎng)絡(luò)結(jié)構(gòu)固定的產(chǎn)品算力很密集脈動陣列可以做得很穩(wěn)如果是多模型多形狀的平臺化需求異構(gòu)組合往往更現(xiàn)實。2. 核心計算模塊細節(jié)算子映射與量化方案如何落地2.1 把卷積拆成硬件能吃的循環(huán)結(jié)構(gòu)神經(jīng)網(wǎng)絡(luò)算子映射到硬件本質(zhì)上是對循環(huán)做變換。卷積的計算其實是一個五層循環(huán)遍歷輸出通道 k、輸入通道 c、輸出高度 h、輸出寬度 w、還有卷積分量 kh 和 kw。硬件上不可能一次性把所有這些循環(huán)都無限制展開所以必須做分塊tiling。舉個例子設(shè)計一個加速器時我常先把卷積抽象成三個循環(huán)塊外層是輸出通道循環(huán)把它分成若干 block中間層是輸入通道循環(huán)做輸入特征圖和權(quán)重的分批加載內(nèi)層是輸出圖上的空間循環(huán)決定 MAC 單元怎么并行、數(shù)據(jù)怎么復用。為什么這么切因為輸出通道決定了權(quán)重能不能駐留在片上。假設(shè)計算陣列寬度是 16輸出通道按 16 一個 block 切那么 3x3x32 的卷積核一次只需要加載 16323*3 4.6KB 的權(quán)重只要 BRAM 放得下整個 block 就只用從 DDR 讀一次權(quán)重。這在外設(shè)帶寬有限的系統(tǒng)里能省下非常多時間。數(shù)據(jù)流方面也都已經(jīng)總結(jié)出一個“stationary”的說法。數(shù)據(jù)的駐留方式?jīng)Q定了你復用哪個數(shù)據(jù)Weight Stationary把權(quán)重固定在一組計算單元里反復使用適合權(quán)重小、特征圖大的卷積Output Stationary把部分和固定在寄存器里適合減少寫回訪存Row Stationary輸入按行流動對于不規(guī)則窗口和 MISD多指令多數(shù)據(jù)類運算更友好。從我自己的設(shè)計經(jīng)驗看輸出駐留往往對邊緣側(cè)推理加速器最直觀因為卷積的中間累加結(jié)果一旦搬回 DDR 就是無謂的帶寬浪費。把部分和直接留在片上等整個輸出通道 block 累加完再一次性寫回是最容易實現(xiàn)也最穩(wěn)的方案。2.2 INT8 量化的工程實現(xiàn)細節(jié)模型量化是推理加速器里繞不開的核心環(huán)節(jié)。FP32 權(quán)重和激活值需要 32 位存儲和處理硬件面積和帶寬成本都太大FP16 對于芯片工藝來說也不算省多數(shù)邊緣/嵌入式推理場景真正實用的是 INT8 甚至更低比特。量化落地有兩個關(guān)鍵點要講清楚。第一個是量化參數(shù)怎么確定。最常用的方案是非對稱線性量化即q round(r / scale) zero_point其中 scale 由量化前后數(shù)值范圍決定zero_point 把實數(shù) 0 映射到整數(shù)。這個映射關(guān)系需要在硬件里做一個減法再做一個乘法通常由加速器的預處理單元來做。scale 的確定有兩種主流手段直接根據(jù)權(quán)重張量的 min/max 計算這個簡單但對激活值誤差較大用一套校準數(shù)據(jù)集跑一遍網(wǎng)絡(luò)統(tǒng)計激活值的分布再用 KL 散度或者百分位數(shù)找閾值。我實際用過第二種方式效果比 min/max 穩(wěn)定得多。拿一個目標檢測模型舉例校準集用 200 張代表性輸入圖激活值量化的精度損失從 2.3% 降到 0.6% 左右。第二點是硬件上必須處理飽和與溢出。INT8 算完的乘積需要更高位的累加器至少是 INT32。假設(shè)一次累加 64 個 INT8 乘積每個乘積最大就是 128*128 1638464 個加起來最大是 1,048,576明顯超出 INT16 的范圍所以累加器必須上 INT32。這部分在寫 RTL 時很容易被忽略導致精度災難。注意量化感知訓練QAT和訓練后量化PTQ在項目里一定要提前區(qū)分。QAT 需要修改訓練代碼成本大但精度高PTQ 只需要推理框架導出時校準適合快速驗證。實際項目里如果 PTQ 精度損失超過 1%再把敏感層挑出來用 QAT 或者混合精度處理一般能控制在可接受范圍。INT4 和更低比特的量化目前在部分加速器上也逐漸有人嘗試但除非你對模型的敏感層做了非常細致的誤差分析否則我還是建議謹慎。INT4 能省一半存取帶寬但精度損失和硬件實現(xiàn)復雜度是雙倍增加不是一個零成本的買賣。2.3 被忽視的那些算子Pooling、Concat 和殘差連接真正的實戰(zhàn)里卷積和全連接反而不是最讓人頭疼的。量化、單算子延遲、硬件時序大部分問題都集中在“看起來很簡單”的操作上。Pooling 的問題在于它往往和卷積不在同一層里做數(shù)據(jù)流可能已經(jīng)走回了 DDR。如果加速器只加速卷積而池化交給 CPU 或其他核來處理那就是一次完整的數(shù)據(jù)發(fā)給 CPU 再傳回來晚了不少。比較有效的做法是把 max pooling 融合進卷積的寫回階段在最后輸出一個像素時直接比大小。Concat 和殘差連接更考驗加速器的地址生成邏輯。這兩個操作不涉及任何乘加只是把多塊片上的內(nèi)存按順序搬到輸出區(qū)域。如果地址生成模塊寫得不夠靈活最常見的現(xiàn)象就是硬件跑某個網(wǎng)絡(luò)時甚至比跑當年模型還不穩(wěn)定因為到處都是對齊錯誤。這三個算子讓我學到最重要的一課加速器不只是算乘法更是一個數(shù)據(jù)搬運和調(diào)度系統(tǒng)。設(shè)計時候給這些“白干活的模塊”留好帶寬和地址映射能力往往比給 MAC 陣列加面積更劃算。3. 實操過程從模型到板卡的四步走完整流程3.1 工具鏈選型和工程搭建HLS 還是 RTL很多剛?cè)腴T的人喜歡糾結(jié)一個問題用 HLS高層次綜合還是寫 RTL我的答案是看階段。如果是做架構(gòu)原型驗證HLS 的效率高很多。你可以在 C 里直接描述卷積循環(huán)的分塊邏輯讓工具綜合出流水線。雖然最終頻率可能不如手寫 RTL但對于驗證“數(shù)據(jù)流對不對”“帶寬夠不夠”這些問題已經(jīng)足夠了。下面給一個實際的 HLS 卷積核心示意偽代碼形式// 以 AXI 接口讀取輸入按 block 方式處理輸出通道 void conv_kernel( hls::streamap_int8 in_stream, hls::streamap_int8 weight_stream, hls::streamap_int32 out_stream, int input_h, int input_w, int num_blocks) { #pragma HLS INTERFACE axis portin_stream #pragma HLS INTERFACE axis portweight_stream #pragma HLS INTERFACE axis portout_stream ap_int32 acc[OUT_TILE_W]; #pragma HLS ARRAY_PARTITION variableacc complete dim1 // 按輸出通道 block 循環(huán) for (int ob 0; ob num_blocks; ob) { // 加載權(quán)重 block // 對空間維度做流水線 for (int h 0; h input_h; h) { for (int w 0; w input_w; w) { #pragma HLS PIPELINE II1 // 讀取輸入并做 MAC 累加 for (int i 0; i OUT_TILE_W; i) { acc[i] in_val * weight_val[i]; } } } } }這種代碼在綜合以后能自動把中間循環(huán)轉(zhuǎn)成流水線。迭代間隔 II1 的意思是每個時鐘周期都要產(chǎn)生一個輸出點這是檢查性能的關(guān)鍵約束。但如果你做的是對面積和頻率極度敏感的正式流片版本那還是要回到 RTL 手寫。HLS 在控制細粒度時序和跨時鐘域時相對吃力調(diào)試波形也不如 RTL 直觀。我的經(jīng)驗是先用 HLS 驗證架構(gòu)可行性再針對關(guān)鍵模塊重寫 RTL。因為內(nèi)容安全問題很多結(jié)構(gòu)我也講不了太細但有一點是確定的工程里結(jié)合兩條路線的結(jié)果比單純選一條路要快很多。3.2 數(shù)據(jù)搬運與片上存儲分配雙緩沖和帶寬計算存儲系統(tǒng)設(shè)計是整個加速器里“看不見但最重要”的部分。片上存儲SRAM/BRAM/URAM的大小直接決定了數(shù)據(jù)復用長度而外部存儲帶寬決定了整個系統(tǒng)能不能跑滿算力。以一個具體參數(shù)計算為例。假設(shè)我們要設(shè)計一個能跑到 100 GOPS 的 INT8 加速器也就是每秒需要 100G 個乘法累加。工作頻率 250MHz 的話每個周期需要完成 400 次 MAC。如果乘法陣列采用 16x16 的形狀每個周期剛好 256 次 MAC接近目標。此時數(shù)據(jù)帶寬要求是多少每個周期從外部讀入兩個 INT8 操作數(shù)輸入和權(quán)重就是 256*2 512B/周期。250MHz 下外部帶寬需要 512 * 250M 128GB/s。這個帶寬遠超過普通 DDR 的能力。所以真正的設(shè)計思路是內(nèi)部數(shù)據(jù)復用。以 3x3 卷積為例同一輸入像素會被多個輸出通道重復使用 16 次權(quán)重則被多個空間位置重復使用多次。合理的數(shù)據(jù)流能把相對最大帶寬降低一個數(shù)量級引入多級緩沖。輸入通道先整批搬到片上片上的 buffer 可以是幾十到幾百 KB做完全部輸出通道的 MAC再丟棄。雙緩沖ping-pong buffer幾乎是標配方法。原理很簡單外部 DMA 正在往 buffer A 搬運下一塊數(shù)據(jù)時計算單元正在從 buffer B 讀取當前塊做計算。這樣搬運和計算可以完全重疊系統(tǒng)吞吐率理論上能做到“搬運時間和計算時間最大值”而不是兩者之和。我在“模擬項目 X”里測過一個極端的 case不做雙緩沖時計算單元有超過 60% 的時間在空轉(zhuǎn)加上雙緩沖后利用率直接拉到 92% 以上。這個提升純粹是存儲調(diào)度帶來的不需要增加任何 MAC 單元。注意片上 SRAM 的容量規(guī)劃要留足余量因為地址對齊、padding 等問題經(jīng)常會讓實際占用比理論值高出 20% 左右。我習慣在規(guī)劃階段就按理論值的 1.3 倍來估算省得后期挪布局。3.3 系統(tǒng)集成與板級驗證從裸循環(huán)到完整鏈路模塊仿真做完了并不代表板子就能跑。真正進入系統(tǒng)集成階段后你會遇到一系列“局部正確但整體掛了”的問題。系統(tǒng)集成要考慮的組件包括DMA 引擎、片間互聯(lián)AXI 總線、中斷控制器、驅(qū)動程序和應用層接口。我通常把集成過程拆成三個階段。第一個階段是裸機驗證。先用片上 SRAM 作為輸入直接通過 JTAG 寫入一些已知的特征圖數(shù)據(jù)跑完計算以后把結(jié)果直接讀到主機端做比對。這一步速度快任何問題都容易定位因為它繞過了一切總線協(xié)議問題。第二個階段是加入 DMA。把輸入輸出都放到 DDR 內(nèi)存里由主機端安排內(nèi)存地址然后觸發(fā)卸載offload任務(wù)。這一步最常踩的坑是指針地址沒有按 64B 對齊或者 burst 長度設(shè)置超過了總線一次傳輸?shù)淖畲笾?。DMA 傳輸一旦出現(xiàn) burst 邊界錯誤數(shù)據(jù)就會靜默錯位最后表現(xiàn)出來就是模型輸出全亂。第三個階段才是跑真實模型。這時要接上運行時runtime軟件框架把權(quán)重文件按照加速器需要的 padding 和重排規(guī)則預處理好。很多模型跑不起來的真正原因不是硬件算力不夠而是權(quán)重格式和運行時接口不匹配。板級驗證時的經(jīng)典順序是先看中斷有沒有來再看 DMA 傳輸長度對不對再看輸出緩沖區(qū)數(shù)據(jù)是不是帶上了垃圾數(shù)據(jù)。按這個順序排查能解決大部分集成階段的問題。4. 調(diào)試與優(yōu)化實錄三個必踩的坑和一個速查表4.1 時序收斂為什么頻率上不去任何硬件設(shè)計做到最后時序收斂都是最熬人的環(huán)節(jié)。最常見的現(xiàn)象是綜合報告一跑最高頻率只有目標 60%一大片關(guān)鍵路徑布滿在乘法陣列的加法樹上。關(guān)鍵字有三類組合邏輯太深通常是因為在一個周期內(nèi)做了 16 個 8 位乘法然后立刻加 64 個數(shù)。解決辦法是手工插入流水寄存器把加法樹切成多級。比如 64 個數(shù)的累加拆成兩級加法每級 32 個輸入這樣單周期組合路徑能縮短一半。扇出過高一個控制信號同時驅(qū)動幾百個加法單元導致布線延遲過大。解決辦法是邏輯復制把同一個控制信號做成多份分區(qū)驅(qū)動。跨時鐘域如果加速器核心跑 250MHz而接口控制器跑 125MHz就必須用異步 FIFO 過渡不能直接采數(shù)。這里最容易踩坑的是 FIFO 的空滿信號判斷多打了幾拍導致數(shù)據(jù)多讀了一次或者漏讀了一次。我在調(diào)一個卷積陣列時花了兩周才把 200MHz 提到 250MHz最后改的僅僅是部分和更新各級的流水寄存器的插入位置。運行頻率提不上去指令再怎么優(yōu)化都沒用所以時序這事值得花時間認真做。心得時序優(yōu)化的正確順序是“架構(gòu)性的優(yōu)化 - 流水線插入 - RTL 微調(diào) - 布局約束”。不要一上來就改幾千行 RTL而是先畫出關(guān)鍵路徑的數(shù)據(jù)流圖看加法樹是否能折疊看是否可以把某個乘法器拆分到多個周期里做。大部分問題出在架構(gòu)而不是語法。4.2 數(shù)據(jù)位寬與溢出問題INT8 乘積的危險區(qū)域只要你做了 INT8 定點化就必然要與溢出問題正面遭遇。我前面已經(jīng)提到累加器要用 INT32但實際中還有幾個更隱蔽的地方。一是權(quán)重量化后的特殊值。如果某個權(quán)重用了對稱量化但 0 和 -128 都被映射成不可表達的值那么計算部分和時會出現(xiàn)極大值。特別是在殘差結(jié)構(gòu)的短路徑中結(jié)果直接被送給下一層時一旦值域超出下一層的量化范圍就會發(fā)生飽和截斷。這類誤差不是單層能看出來的它會順著殘差逐層累積到最后的輸出就是幾個關(guān)鍵類別分數(shù)全亂了。二是乘法陣列內(nèi)部如果用了截斷truncation而不是四舍五入round部分和的誤差也會被放大。我做過一個對比實驗累加結(jié)果不采四舍五入直接截斷模型的 Top-1 精度下降 0.4%改成四舍五入后完全恢復。這個細節(jié)很多人會在一開始忽略。三是非線性激活函數(shù)的實現(xiàn)。ReLU 的硬件實現(xiàn)既可以是 clamp 到 0 再飽和也可以直接做符號位判斷。如果檢測到負數(shù)時沒有正確飽和到 0累積誤差也會越來越大。排查這類問題的方法也很簡單把硬件輸出保存成文本在主機端用 Python 模擬同一位寬的定點數(shù)運算逐層對比部分和的差異。一旦定位到誤差開始出現(xiàn)的那一層就檢查該層的近似實現(xiàn)。4.3 模型精度下降量化后掉三個點的攻堅記錄有一次我們把一個檢測模型從 FP32 切到 INT8測試精度掉了 3.1 個點。按經(jīng)驗這個幅度已經(jīng)算比較大了于是開始逐層排查。首先檢查的是校準集。原來的校準集里都是室內(nèi)圖像數(shù)據(jù)但實際部署場景有一半是室外逆光的圖像。激活值分布在暗光區(qū)域沒有校準到于是量化 scale 整體偏差。換了一套混合場景的校準集后精度損失降到 1.8%。接下來看 BNBatchNorm融合問題。很多框架導出的模型里還帶 BN 層BN 在推理階段可以完全融合到卷積權(quán)重里省掉一整層的計算。但如果融合操作做晚了量化后的 BN 計算會引入一個額外的大數(shù)量級乘法嚴重拉大數(shù)值誤差。把 BN 融入到卷積再做量化精度損失又降了 0.5%。最后發(fā)現(xiàn)某一層殘差分支的輸出值域特別大用 INT8 表述后精度損失的方差都集中在這里。把那層提升為 INT16再跑一遍最終精度損失降到 0.6% 以內(nèi)。這個問題給我一個很深的感觸逐層掃描配合統(tǒng)計信息比“全局調(diào)一調(diào) scale 再試一次”要高效得多。硬件加速器的精度問題通常都不是一個玄學而是某幾個敏感層的具體問題。4.4 常見問題速查表先把調(diào)試現(xiàn)場整理成表格方便以后遇到問題時直接查現(xiàn)象可能原因排查順序解決辦法板卡輸出全為 0DMA 沒觸發(fā)或地址對齊錯先查中斷再查 DMA 描述符檢查地址對齊和 burst 長度配置模型輸出隨機錯亂數(shù)據(jù)位寬截斷或量化溢出對比單層定制結(jié)果用四舍五入累加查 INT32 累加器寬度推理速度不達預期計算和搬運沒有重疊看仿真波形中計算單元的 busy 信號增加雙緩沖和 DMA 下發(fā)預取板級偶發(fā)抖動跨時鐘域沒做同步抓不定態(tài)波形加異步 FIFO多拍同步后采樣頻率跑不上去組合邏輯過深或扇出過高分析關(guān)鍵路徑插入流水寄存器、復制控制信號量化后精度驟降校準集分布不符統(tǒng)計每層激活值范圍換校準集、對敏感層做混合精度這張表基本覆蓋了常見的軟硬件協(xié)同問題。每次做完一塊加速器我都會把它更新一次項目做得越多排查速度就越快。最后再分享一點個人體會如果說整個設(shè)計流程里有什么最反直覺的經(jīng)驗那就是真正決定一個推理加速器能否落地的往往不是那套 16x16 的乘法陣列而是外圍的存儲調(diào)度、DMA 設(shè)計和時序收斂處理。我見過有人在陣列面積上摳了一個月結(jié)果因為緩存分配不合理導致利用率只有一半。反過來把數(shù)據(jù)流設(shè)計得足夠穩(wěn)系統(tǒng)級的收益經(jīng)常比單點算力翻倍還高。另外調(diào)試過程里最實在的小技巧是保存一份“軟件浮點中間結(jié)果綁定到硬件內(nèi)部節(jié)點”的測試環(huán)境。就是說在硬件仿真階段就把每一層的期望值提前算好然后設(shè)計 RTL 測試時直接比對中間信號。這樣任何一層出了問題能立刻定位是計算問題、數(shù)據(jù)搬運問題還是量化問題。很多項目因為缺少這種閉環(huán)調(diào)試手段最后都陷入“調(diào)一個參數(shù)、看一個現(xiàn)象、再猜下一個原因”的泥潭。這塊內(nèi)容的擴展方向其實也很多比如低比特量化、稀疏化加速、存內(nèi)計算甚至多卡互聯(lián)上的推理調(diào)度。每個方向都是一個不小的坑但基礎(chǔ)的數(shù)據(jù)流和硬件設(shè)計思路完全一致。希望這篇文章能幫你在起步階段少幾次重寫 RTL 的沖動多跑通幾個真正能用的板卡。