設(shè)計(jì)與驗(yàn)收)
簡(jiǎn)介《基于存算一體集成芯片的大模型專用硬件架構(gòu)》是一份面向大模型硬件架構(gòu)研究者、芯片設(shè)計(jì)工程師與高校相關(guān)專業(yè)師生的學(xué)術(shù)論文PDF聚焦大模型部署中的數(shù)據(jù)傳輸帶寬瓶頸與數(shù)據(jù)中心能耗壓力。文件共1個(gè)PDF約3.49MB完整收錄何斯琪、穆琛、陳遲曉發(fā)表于《中興通訊技術(shù)》2024年第30卷第2期的文章便于檢索、引用與精讀。內(nèi)容圍繞存算一體集成芯片與集成芯粒方案展開分析傳統(tǒng)計(jì)算與存儲(chǔ)分離架構(gòu)的局限并討論輕量化—存內(nèi)壓縮協(xié)同設(shè)計(jì)、稀疏網(wǎng)絡(luò)在存算一體硬件上的稠密映射等實(shí)現(xiàn)思路對(duì)理解大模型推理能效優(yōu)化和專用硬件設(shè)計(jì)具有參考價(jià)值。已有140人學(xué)習(xí)適合作為架構(gòu)調(diào)研、論文寫作或技術(shù)方案論證的參考資料。1. 為什么大模型推理通常會(huì)卡在“搬數(shù)據(jù)”上一筆帶寬賬決定的架構(gòu)選型給一個(gè) 70 億參數(shù)的大模型做推理硬件方案最先被推翻的往往不是算力預(yù)算而是帶寬預(yù)算。按每秒一萬(wàn)個(gè)令牌的實(shí)時(shí)速率計(jì)算僅僅把權(quán)重從存儲(chǔ)搬到計(jì)算單元就需要每秒幾十太字節(jié)的搬運(yùn)能力而當(dāng)前主流的高帶寬存儲(chǔ)封裝單棧只有每秒零點(diǎn)幾太字節(jié)的量級(jí)多棧堆疊也補(bǔ)不上這個(gè)缺口。這個(gè)缺口不是靠堆容量能抹平的它直接決定方案走向計(jì)算和存儲(chǔ)到底要不要貼在一起。基于存算一體集成芯片的大模型專用硬件架構(gòu)解決的就是這筆搬運(yùn)賬把權(quán)重固定在存儲(chǔ)陣列里讓乘加在數(shù)據(jù)所在的位置發(fā)生再用芯粒集成繞開單芯片的面積與良率天花板。這套思路適合兩類人要做一份完整硬件架構(gòu)畢設(shè)或課設(shè)的學(xué)生以及要給推理加速卡做選型論證的工程師。2. 帶寬瓶頸如何計(jì)算從參數(shù)規(guī)模到芯粒接口速率架構(gòu)方案里最容易空泛、也最容易被導(dǎo)師或評(píng)審追問的地方就是“帶寬不夠”這四個(gè)字。不夠多少、缺在哪個(gè)環(huán)節(jié)、換成什么結(jié)構(gòu)能補(bǔ)上多少必須能算。這一章把這筆賬拆成三層模型側(cè)的增長(zhǎng)曲線、系統(tǒng)側(cè)的搬運(yùn)量、器件側(cè)的算力存儲(chǔ)比。三層算完架構(gòu)選型就不是拍腦袋而是被數(shù)字推出來(lái)的。2.1 參數(shù)規(guī)模、算力需求與單芯片面積的三條天花板先看模型側(cè)。以注意力機(jī)制為基礎(chǔ)的大模型在近幾年呈現(xiàn)出一種近乎失控的增長(zhǎng)參數(shù)規(guī)模大約每?jī)赡暝鲩L(zhǎng) 240 倍對(duì)應(yīng)的算力需求增長(zhǎng)接近 750 倍。而通用硬件每?jī)赡甏蠹s只有 3.1 倍的提升并且已經(jīng)逼近物理極限。三條曲線放在一張圖上缺口就是逐年拉大的剪刀差這個(gè)剪刀差最終會(huì)以“推理成本”的形式落到數(shù)據(jù)中心賬單上。再看制造側(cè)。傳統(tǒng)超大規(guī)模、超大面積的單芯片 SoC 方案在面積繼續(xù)放大時(shí)會(huì)同時(shí)遇到四個(gè)問題晶圓利用率下降、良率隨面積指數(shù)衰減、驗(yàn)證復(fù)雜度失控、設(shè)計(jì)成本激增。更硬的一條約束是光刻12 英寸晶圓在光刻機(jī)的要求下單顆芯片的最大設(shè)計(jì)面積被限制在 858 mm2 左右掩膜版尺寸本身就是一堵墻。也就是說即使不計(jì)成本和良率單芯片路線在面積上也無(wú)法再大幅擴(kuò)展。第三條天花板來(lái)自封裝與接口。大模型推理的瓶頸并不在乘加單元本身而在于權(quán)重需要從片外存儲(chǔ)反復(fù)搬到計(jì)算單元。這條通路的帶寬由封裝、接口速率和引腳數(shù)共同決定屬于典型的“物理上無(wú)法在短期內(nèi)倍增”的環(huán)節(jié)。三條天花板疊加結(jié)論就比較清楚了繼續(xù)沿著單芯片 SoC 增加算力很難可以轉(zhuǎn)向“把大芯片拆成小芯片再集成”的路線。集成芯粒的思路是先把多個(gè)小顆粒芯片獨(dú)立設(shè)計(jì)、獨(dú)立實(shí)現(xiàn)再通過先進(jìn)封裝重新組裝成系統(tǒng)。這樣做的好處很直接單顆芯粒面積小、良率高、可復(fù)用設(shè)計(jì)周期短壞處是片間互聯(lián)和封裝成為新的約束項(xiàng)必須在架構(gòu)階段就一并設(shè)計(jì)不能等到后端再補(bǔ)。2.2 用令牌速率反推帶寬需求一份可以自行復(fù)算的推導(dǎo)模型側(cè)的賬用一份可執(zhí)行腳本算最清楚。下面這段腳本把“參數(shù)量 → 每令牌搬運(yùn)字節(jié)數(shù) → 帶寬需求”串起來(lái)并且把幾種典型架構(gòu)的接口速率放在一起對(duì)比。# 大模型推理帶寬/算力/存儲(chǔ)預(yù)算復(fù)算腳本 # 所有參數(shù)均可替換用于方案階段快速做架構(gòu)對(duì)比 def inference_budget(params_billion, token_rate, weight_bits8, hidden4096, ffn11008, layers32, target_topsNone): params_billion: 模型參數(shù)量十億 token_rate: 目標(biāo)令牌速率令牌/秒 weight_bits: 權(quán)重位寬INT8 取 8 hidden/ffn/layers: 全連接層維度用于核算力與存儲(chǔ) # 1) 每個(gè)令牌需要搬運(yùn)的權(quán)重字節(jié)數(shù)解碼階段每令牌過一遍全部權(quán)重 bytes_per_token params_billion * 1e9 * weight_bits / 8 # 2) 帶寬需求 單令牌搬運(yùn)量 × 令牌速率 bw_Bps bytes_per_token * token_rate bw_TBps bw_Bps / 1e12 # 3) 三層連續(xù)全連接層FC0/FC1/FC2的算力與存儲(chǔ) macs_per_layer (hidden * ffn ffn * hidden hidden * hidden) total_macs macs_per_layer * layers tops total_macs * token_rate * 2 / 1e12 # 1 MAC 2 ops capacity_GB total_macs * weight_bits / 8 / 1e9 # 4) 算力存儲(chǔ)比單位 ops/byte數(shù)值上等于令牌速率 ratio tops * 1e12 / (capacity_GB * 1e9) return { 帶寬需求(TB/s): round(bw_TBps, 3), 算力需求(TOPS): round(tops, 2), 容量需求(GB): round(capacity_GB, 3), 算力存儲(chǔ)比(ops/byte): round(ratio, 1), } # 70 億參數(shù)模型1 萬(wàn)令牌/秒的實(shí)時(shí)推理目標(biāo) print(inference_budget(7, 10_000)) # 輸出約帶寬 64 TB/s 量級(jí)算力 68 TOPS容量 3.4 GB腳本里最關(guān)鍵的一行是bytes_per_token。解碼階段每個(gè)令牌都要把全部權(quán)重過一遍所以帶寬需求等于“參數(shù)量 × 位寬 ÷ 8 × 令牌速率”與批大小、序列長(zhǎng)度都無(wú)關(guān)只和模型尺寸與目標(biāo)速率掛鉤。這解釋了一個(gè)常被誤判的現(xiàn)象把令牌速率從每秒 1 個(gè)提到每秒 1 萬(wàn)個(gè)算力需求只漲到 68 TOPS對(duì)一顆芯片來(lái)說并不夸張但帶寬需求漲到了 64 TB/s對(duì)任何現(xiàn)有封裝來(lái)說都不現(xiàn)實(shí)。參數(shù)說明與調(diào)參建議weight_bits從 8 降到 4帶寬直接減半這也是權(quán)重量化在推理側(cè)收益立竿見影的原因token_rate是業(yè)務(wù)指標(biāo)做方案論證時(shí)應(yīng)先固定它再倒推硬件而不是先有硬件再編速率。算力存儲(chǔ)比那一項(xiàng)的輸出值在數(shù)值上恰好等于令牌速率這個(gè)巧合背后是“每字節(jié)權(quán)重只參與一次乘加”的解碼特性后面第 3 章會(huì)用它來(lái)反推存邊架構(gòu)的并行度設(shè)計(jì)。對(duì)比之下采用分離式計(jì)算架構(gòu)時(shí)GPU 與高帶寬存儲(chǔ)之間的數(shù)據(jù)搬運(yùn)量就是上面算出來(lái)的量級(jí)而在存算一體架構(gòu)里權(quán)重常駐陣列接口上只需要搬運(yùn)輸入激活和輸出結(jié)果同樣令牌速率下帶寬需求能壓到每秒幾十吉比特的量級(jí)差距超過三個(gè)數(shù)量級(jí)。這不是靠“帶寬優(yōu)化技巧”省出來(lái)的而是數(shù)據(jù)通路結(jié)構(gòu)變了。2.3 算力存儲(chǔ)比大模型和卷積網(wǎng)絡(luò)比較數(shù)值時(shí)需要注意第二個(gè)容易被誤用的指標(biāo)是算力存儲(chǔ)比。拿前面算出的 68 TOPS 與 3.4 GB 相除得到約 20 ops/byte。而經(jīng)典卷積網(wǎng)絡(luò)在圖像任務(wù)上的算力存儲(chǔ)比要高出一到兩個(gè)數(shù)量級(jí)通常在每千字節(jié)上百 GOPS 的量級(jí)。兩者不是一個(gè)量級(jí)的負(fù)載卷積網(wǎng)絡(luò)是計(jì)算密集大模型解碼是訪存密集。這個(gè)差別直接決定了存儲(chǔ)介質(zhì)和陣列結(jié)構(gòu)的選法。經(jīng)典交叉陣列做存內(nèi)計(jì)算時(shí)算力存儲(chǔ)比約等于“時(shí)鐘頻率 × 2”因?yàn)橐淮侮嚵屑?jí)乘加在一個(gè)時(shí)鐘周期內(nèi)完成而陣列深度決定了它能裝多少權(quán)重。當(dāng)負(fù)載的算力存儲(chǔ)比遠(yuǎn)低于這個(gè)值時(shí)交叉陣列會(huì)長(zhǎng)時(shí)間處于“等數(shù)據(jù)”的狀態(tài)并行度上不去。針對(duì)這一點(diǎn)存邊計(jì)算的做法是把乘加邏輯分布在片上權(quán)重緩沖 SRAM 的邊緣讓算力存儲(chǔ)比變成“時(shí)鐘頻率 × 2 ÷ 存儲(chǔ)深度”。存儲(chǔ)深度可以按宏單元的容量來(lái)配等于把算力存儲(chǔ)比做成了一個(gè)可調(diào)參數(shù)而不是被陣列結(jié)構(gòu)鎖死。這個(gè)調(diào)整聽起來(lái)只是公式變化落到實(shí)現(xiàn)上意味著權(quán)重可以按輸入通道方向切塊、展平后存進(jìn)不同列輸出通道方向的并行度靠增加宏單元數(shù)量來(lái)堆近存計(jì)算里成熟的數(shù)據(jù)流映射方法可以直接搬過來(lái)用。提示做方案對(duì)比時(shí)務(wù)必把“算力存儲(chǔ)比”和“峰值算力”分開列。只報(bào)峰值算力的方案在解碼類負(fù)載上通常兌現(xiàn)不了。3. 總體架構(gòu)設(shè)計(jì)CPU 與存算一體芯粒的分工與數(shù)據(jù)流算完賬架構(gòu)的骨架其實(shí)已經(jīng)被約束住了主機(jī)側(cè)負(fù)責(zé)調(diào)度、采樣、非矩陣類算子加速側(cè)負(fù)責(zé)全連接層和注意力的矩陣運(yùn)算兩者之間只走輸入輸出不走權(quán)重。這一章把這個(gè)骨架展開成可實(shí)現(xiàn)的模塊劃分、數(shù)據(jù)流映射和多芯粒擴(kuò)展方案。3.1 存算分離與存算一體的數(shù)據(jù)通路對(duì)比分離式架構(gòu)的數(shù)據(jù)通路是主機(jī) CPU 通過高速串行總線把請(qǐng)求發(fā)給加速卡加速卡上的計(jì)算芯片從外部高帶寬存儲(chǔ)里反復(fù)讀取權(quán)重算完把結(jié)果寫回再經(jīng)總線返回。這條通路上真正昂貴的不是計(jì)算而是“權(quán)重讀一遍、用一次、丟掉、下個(gè)令牌再讀一遍”。存算一體架構(gòu)把這條通路改成主機(jī) CPU 通過同一套總線把輸入激活送到存算加速卡權(quán)重常駐在加速卡的存算陣列里不動(dòng)乘加在陣列內(nèi)部完成只把輸出激活送回主機(jī)。外部存儲(chǔ)的角色從“權(quán)重的家”降級(jí)為“激活的暫存”容量需求和帶寬需求同時(shí)下降一個(gè)數(shù)量級(jí)。這里有一個(gè)容易在設(shè)計(jì)文檔里寫漏的點(diǎn)主機(jī)側(cè)的矢量計(jì)算單元和小算力 NPU 并不是冗余的。注意力模塊里的 softmax、層歸一化、掩碼、采樣這些操作計(jì)算量不大但訪存模式零碎放在存算陣列里做反而低效。合理的分工是主機(jī)側(cè)處理這些“非規(guī)則但輕量”的算子存算側(cè)只處理“規(guī)則且重”的矩陣乘。方案里如果只畫一顆存算芯片評(píng)閱時(shí)基本會(huì)被問“softmax 在哪算”。從帶寬對(duì)比看同為每秒一萬(wàn)令牌的目標(biāo)速率分離式架構(gòu)需要幾十太字節(jié)每秒的片外帶寬存算一體架構(gòu)只需要每芯粒每秒幾十吉比特量級(jí)的接口速率節(jié)省幅度超過三個(gè)數(shù)量級(jí)。這個(gè)數(shù)字也解釋了為什么單芯粒接口速度可以做得比較保守從而繞開一部分高速接口的工藝與管制約束。3.2 存邊計(jì)算架構(gòu)把乘加邏輯挪到 SRAM 的邊緣存邊計(jì)算的物理形態(tài)是一片 SRAM 陣列邊緣掛一排乘加單元和歸約樹權(quán)重按行或按列靜態(tài)存放輸入激活從一側(cè)流入部分和從另一側(cè)匯出。它的好處是既能用上 SRAM 的高能效又不像交叉陣列那樣被陣列深度綁死并行度。數(shù)據(jù)流映射的具體做法是權(quán)重在計(jì)算開始前預(yù)先加載進(jìn)宏單元沿輸入通道方向切塊后展平存入宏單元的不同列輸出通道方向的并行度靠堆多個(gè)宏單元實(shí)現(xiàn)形成空間并行。下面這段配置腳本描述了一次映射的參數(shù)組織方式實(shí)際綜合時(shí)可以把它轉(zhuǎn)成宏單元的配置寄存器表。# 存邊宏單元的權(quán)重組裝與映射配置 # 目標(biāo)把一層全連接權(quán)重切成列塊鋪到 N 個(gè) COMB 宏上 def build_mapping(in_ch, out_ch, macro_cols64, macro_rows256, macros4): in_ch / out_ch: 該層輸入、輸出通道數(shù) macro_cols: 單個(gè)宏單元一行能放多少個(gè)權(quán)重列方向 macro_rows: 單個(gè)宏單元能放多少行深度 macros: 并行使用的宏單元顆數(shù) # 每個(gè)輸出通道的權(quán)重沿輸入通道切塊每塊長(zhǎng)度 macro_cols blocks_per_out (in_ch macro_cols - 1) // macro_cols # 輸出通道按宏數(shù)量均分實(shí)現(xiàn)輸出方向的空間并行 outs_per_macro (out_ch macros - 1) // macros mapping [] for m in range(macros): for b in range(blocks_per_out): mapping.append({ macro: m, block: b, in_start: b * macro_cols, in_end: min((b 1) * macro_cols, in_ch), out_start: m * outs_per_macro, out_end: min((m 1) * outs_per_macro, out_ch), depth_needed: outs_per_macro, # 占用宏單元行數(shù) }) # 校驗(yàn)占用行數(shù)不能超過宏深度否則需要增加宏或二次切分 assert all(x[depth_needed] macro_rows for x in mapping), 宏深度不足 return mapping for item in build_mapping(4096, 11008)[:3]: print(item)macro_cols決定了單次陣列操作的向量寬度macro_rows決定了能同時(shí)駐留多少個(gè)輸出通道的權(quán)重macros決定了輸出方向的空間并行度。三者共同決定算力存儲(chǔ)比。實(shí)踐中的調(diào)參順序是先按片上 SRAM 總面積和工藝節(jié)點(diǎn)定macro_rows再按目標(biāo)算力定macros最后用macro_cols對(duì)齊位寬和累加器位寬。斷言那一行不是裝飾depth_needed超過宏深度時(shí)如果只增加宏數(shù)量而不調(diào)整切分策略會(huì)得到一堆溢出越界的地址仿真階段表現(xiàn)為隨機(jī)錯(cuò)值非常難查。3.3 多芯粒集成的劃分與擴(kuò)展性單顆芯粒的容量是有上限的。以 12/14 nm 工藝估算一顆 10 TOPS、200 MB 存儲(chǔ)的芯粒計(jì)算電路面積約 8 mm2存儲(chǔ)面積約 300 mm2折算下來(lái)算力密度只有 0.0325 TOPS/mm2。這個(gè)數(shù)字說明兩件事一是存儲(chǔ)面積占絕對(duì)主導(dǎo)想提高容量只能堆芯粒二是算力密度低不是設(shè)計(jì)失誤而是這類架構(gòu)的固有特征方案文檔里必須主動(dòng)解釋否則會(huì)被當(dāng)成缺陷。四顆 65 nm 的 SRAM 存邊計(jì)算芯粒通過扇出型 2.5D 封裝集成在一起是這套方案里比較典型的一個(gè)規(guī)模點(diǎn)算力和容量都能獲得接近線性的提升封裝工藝也相對(duì)成熟。超過四顆之后映射方法需要重新設(shè)計(jì)否則算力不會(huì)隨芯粒數(shù)量線性增長(zhǎng)原因在于芯粒間的激活廣播和部分和歸約開始成為瓶頸互聯(lián)拓?fù)浜颓蟹至6榷家?。芯粒劃分的一般原則可以歸納成三條把同一層的權(quán)重盡量聚在同一顆芯粒上減少跨芯粒的權(quán)重訪問把需要頻繁交換部分和的通道切在同一封裝內(nèi)避免走封裝外總線給每顆芯粒留出獨(dú)立的激活緩沖避免共享緩沖成為串行點(diǎn)。3.4 單芯粒預(yù)算表方案文檔里必須有的那張表評(píng)審看方案第一眼找的就是這張表。缺了它后面的模塊設(shè)計(jì)再細(xì)也會(huì)被質(zhì)疑“沒有量化依據(jù)”。項(xiàng)目取值說明工藝節(jié)點(diǎn)12/14 nm 估算決定面積與能效基線單芯粒算力10 TOPSINT8 等效單芯粒容量200 MB片上 SRAM 權(quán)重區(qū)計(jì)算電路面積約 8 mm2乘加陣列與歸約樹存儲(chǔ)面積約 300 mm2占主導(dǎo)決定了芯粒尺寸算力密度0.0325 TOPS/mm2該類架構(gòu)的常態(tài)值集成方式扇出型 2.5D 封裝四芯粒為一個(gè)規(guī)模點(diǎn)片間接口每芯粒 3264 Gbit/s遠(yuǎn)低于分離式架構(gòu)要求表格里最后一行是這套方案最容易被忽略的價(jià)值點(diǎn)因?yàn)闄?quán)重不出片片間接口只需要承載激活和部分和速率要求被壓到很低封裝難度和功耗隨之下降。做方案論證時(shí)應(yīng)該把這一行單獨(dú)強(qiáng)調(diào)它比“峰值算力多少 TOPS”更能說明架構(gòu)的合理性。4. 輕量化與存內(nèi)壓縮的協(xié)同設(shè)計(jì)稀疏網(wǎng)絡(luò)在存算陣列上如何稠密映射前面三章解決的是“權(quán)重怎么不搬家”這一章解決“權(quán)重怎么少占地方”。稀疏化本身并不新鮮難點(diǎn)在于把非結(jié)構(gòu)化剪枝后的稀疏矩陣直接塞進(jìn)規(guī)則陣列零值仍然要占存儲(chǔ)單元壓縮等于白做。這一章給出的是軟硬件協(xié)同的一條路徑剪枝策略、位掩碼編碼、路由網(wǎng)絡(luò)三件事必須一起設(shè)計(jì)。4.1 剪枝子組大小與稀疏率的確定方法稀疏化的兩個(gè)關(guān)鍵參數(shù)是子組大小和稀疏率。子組太大壓縮粒度粗精度掉得快子組太小解碼邏輯復(fù)雜位掩碼本身的開銷就吃掉收益。取值不能靠猜要在目標(biāo)任務(wù)上做一輪掃描。掃描的做法是固定子組大小逐步提高稀疏率觀察驗(yàn)證集指標(biāo)找到指標(biāo)開始下降的拐點(diǎn)再把拐點(diǎn)往回退一檔作為工作點(diǎn)。在一組字符級(jí)語(yǔ)言建模任務(wù)的實(shí)驗(yàn)中子組大小取 32、修剪掉四分之三的權(quán)重時(shí)網(wǎng)絡(luò)性能基本保持不變于是工作點(diǎn)被定為“子組 32、稀疏率 75%”。這個(gè)結(jié)果不是普適常數(shù)換模型、換任務(wù)都要重掃但它給出了一個(gè)合理的出發(fā)區(qū)間。注意精度拐點(diǎn)必須用最終任務(wù)的指標(biāo)來(lái)判定不能用訓(xùn)練損失代替。訓(xùn)練損失平穩(wěn)而下游指標(biāo)下降的情況在這類壓縮里非常常見。4.2 位掩碼編碼與稠密權(quán)重的存內(nèi)組織確定了工作點(diǎn)編碼方式就固定下來(lái)了權(quán)重向量按子組劃分每組按預(yù)定義稀疏度修剪剪枝后的非零權(quán)重壓成一個(gè)稠密向量同時(shí)生成一個(gè)等長(zhǎng)的比特掩碼用來(lái)指示稠密權(quán)重在原始向量里的位置。陣列里只存稠密向量掩碼存在旁邊的掩碼存儲(chǔ)里兩者一一對(duì)應(yīng)。下面這段腳本實(shí)現(xiàn)了完整的編碼與解碼校驗(yàn)可以直接拿來(lái)驗(yàn)證編碼邏輯有沒有寫錯(cuò)。import random def prune_group(weights, sparsity0.75): 按絕對(duì)值保留最大的 (1-sparsity) 比例權(quán)重返回稠密向量與位掩碼 n len(weights) keep max(1, int(round(n * (1 - sparsity)))) idx sorted(range(n), keylambda i: -abs(weights[i]))[:keep] keep_set set(idx) mask [1 if i in keep_set else 0 for i in range(n)] # 1 表示保留 dense [weights[i] for i in range(n) if mask[i] 1] return dense, mask def prefix_popcount(mask): 前綴 popcount輸出每個(gè)位置之前含自身位置前1 的個(gè)數(shù) 用于把掩碼位置翻譯成稠密向量的下標(biāo) out, cnt [], 0 for b in mask: out.append(cnt) cnt b return out # 校驗(yàn)稠密向量第 k 個(gè)元素應(yīng)該落在掩碼第 prefix_popcount[mask[i]] 位 weights [round(random.uniform(-1, 1), 3) for _ in range(32)] dense, mask prune_group(weights, 0.75) pp prefix_popcount(mask) for i, b in enumerate(mask): if b 1: assert dense[pp[i]] weights[i], 編碼/解碼不一致 print(保留權(quán)重?cái)?shù):, len(dense), 掩碼長(zhǎng)度:, len(mask))這段代碼的核心是prefix_popcount。它把“原向量第 i 位”翻譯成“稠密向量的第幾個(gè)元素”硬件里的實(shí)現(xiàn)就是一條掃描鏈加一個(gè)計(jì)數(shù)器。校驗(yàn)斷言那一行是必須的編碼端和解碼端只要對(duì)“前綴是否含自身”的理解差一位整個(gè)陣列的輸出就會(huì)整體偏移一個(gè)位置現(xiàn)象是結(jié)果“看起來(lái)差不多但精度掉一截”屬于最費(fèi)時(shí)間的 bug 之一。4.3 蝶形路由網(wǎng)絡(luò)前綴 popcount 與左旋轉(zhuǎn)補(bǔ)碼稠密權(quán)重準(zhǔn)備好了輸入激活還得跟著一起“擠”。原始輸入特征里被剪掉權(quán)重對(duì)應(yīng)的那些輸入不需要參與計(jì)算必須從輸入向量里挑出來(lái)、按順序排好送進(jìn)陣列。這個(gè)重排用蝶形網(wǎng)絡(luò)實(shí)現(xiàn)多級(jí) 2 選 1 的數(shù)據(jù)分發(fā)單元控制信號(hào)由解碼器實(shí)時(shí)產(chǎn)生。解碼器接收位掩碼生成每一級(jí)蝶形的控制比特涉及兩個(gè)操作。第一個(gè)是前綴 popcount掃描掩碼序列輸出當(dāng)前位置之前 1 的總數(shù)用來(lái)確定某個(gè)數(shù)據(jù)該被送到第幾個(gè)輸出口。第二個(gè)是左旋轉(zhuǎn)補(bǔ)碼LRCW本質(zhì)是一次循環(huán)左移唯一區(qū)別是移位量在運(yùn)算過程中以補(bǔ)碼形式表示用來(lái)在每一級(jí)修正路由偏移。def lrcw_shift(src_bits, shift, width): 左旋轉(zhuǎn)補(bǔ)碼循環(huán)左移移位量按補(bǔ)碼解釋負(fù)值等價(jià)于反向旋轉(zhuǎn) shift % width # 補(bǔ)碼語(yǔ)義下先歸一化到位寬范圍 return ((src_bits shift) | (src_bits (width - shift))) ((1 width) - 1) def butterfly_route(inputs, mask, stages): 按位掩碼把輸入特征路由到稠密位置stages 為蝶形級(jí)數(shù) width len(inputs) assert width (1 stages), 輸入長(zhǎng)度需為 2 的整數(shù)次冪 idx [i for i, b in enumerate(mask) if b 1] routed [inputs[i] for i in idx] # 硬件中每級(jí)只做一次局部交換這里用軟件等價(jià)描述便于驗(yàn)證 ctrl lrcw_shift(0b0011, 1, 4) assert ctrl in range(1 4) return routed print(butterfly_route(list(range(8)), [0,1,1,0,1,0,0,1], 3)) # 期望輸出[1, 2, 4, 7]軟件描述里看不出并行性硬件上則是每一級(jí)只做一次相鄰或跨組的 2 選 1 交換級(jí)數(shù)等于輸入長(zhǎng)度的以 2 為底對(duì)數(shù)延遲對(duì)輸入長(zhǎng)度是對(duì)數(shù)關(guān)系。設(shè)計(jì)時(shí)的關(guān)鍵約束是路由網(wǎng)絡(luò)的控制信號(hào)必須在同一拍內(nèi)產(chǎn)生不能跨拍否則數(shù)據(jù)流水的節(jié)拍會(huì)被解碼器拖住。這也是為什么解碼器要用純組合邏輯加前綴樹而不是塞進(jìn)一條通用流水線。4.4 28 nm 驗(yàn)證芯片的指標(biāo)拆解這套協(xié)同設(shè)計(jì)在一顆 28 nm 工藝的驗(yàn)證芯片上做過實(shí)現(xiàn)指標(biāo)可以按維度拆開看。指標(biāo)數(shù)值讀法工藝 / 精度28 nm / INT8定制 SRAM 位單元陣列規(guī)模96 kB宏單元 80 kB 量級(jí)、系統(tǒng)緩沖 64 kB 量級(jí)工作頻率320 MHz電壓 0.641.03 V總功耗250.65 mW含控制器與全局聚合峰值性能3.3 TOPS已計(jì)入 75% 稀疏率芯片面積3.93 mm2含 8 個(gè)功能單元與特征存儲(chǔ)面積效率0.85 TOPS/mm2峰值性能 ÷ 面積系統(tǒng)能效11.83 / 25.22 TOPS/W生成 QKV / 整體注意力能效拆成兩個(gè)值來(lái)報(bào)是有意為之。生成查詢、鍵、值矩陣這一步是全連接層的稠密計(jì)算能效體現(xiàn)的是陣列本身的水平整體注意力包含了路由與聚合的開銷能效體現(xiàn)的是協(xié)同設(shè)計(jì)的水平。兩個(gè)數(shù)一起看才能判斷存內(nèi)壓縮到底有沒有帶來(lái)凈收益。如果只報(bào)前一個(gè)數(shù)評(píng)審很容易問“壓縮開銷算了嗎”答不上來(lái)就會(huì)顯得方案不完整。5. 常見問題與排查從建模到流片最容易出問題的五個(gè)點(diǎn)方案寫到這一章之前都是“應(yīng)該怎樣”這一章說“實(shí)際會(huì)怎樣”。下面五條都是在同類項(xiàng)目里反復(fù)出現(xiàn)的問題按“現(xiàn)象 → 原因 → 解決”組織做成檢查清單比任何設(shè)計(jì)原則都省時(shí)間。5.1 稀疏率按經(jīng)驗(yàn)設(shè)定精度下降后才發(fā)現(xiàn)子組大小取錯(cuò)現(xiàn)象編碼和陣列仿真都通過功能正確但任務(wù)指標(biāo)比基線低了兩三個(gè)點(diǎn)重訓(xùn)也拉不回來(lái)。原因子組大小是按“常見值”設(shè)定的沒有在目標(biāo)任務(wù)上掃過拐點(diǎn)。子組過大時(shí)一個(gè)子組里被剪掉的權(quán)重分布與權(quán)重的重要性分布不匹配等價(jià)于做了一次粗粒度的結(jié)構(gòu)破壞子組過小時(shí)位掩碼的相對(duì)開銷變高壓縮收益被吃掉而精度又沒變好。解決把子組大小和稀疏率做成二維掃描表每一格記錄指標(biāo)取“指標(biāo)不下降前提下壓縮比最高”的那一格。掃描成本遠(yuǎn)低于后期返工。經(jīng)驗(yàn)上字符級(jí)建模任務(wù)在子組 32、稀疏率 75% 附近比較穩(wěn)但換任務(wù)必須重掃。5.2 交叉陣列里零權(quán)重未清干凈稠密映射實(shí)際未生效現(xiàn)象陣列規(guī)??雌饋?lái)沒變功耗反而略微上升性能提升遠(yuǎn)小于預(yù)期。原因非結(jié)構(gòu)化剪枝后的矩陣在映射到規(guī)則陣列時(shí)被剪掉的位置如果沒有做顯式清零或旁路存儲(chǔ)單元里殘留的是舊值或隨機(jī)初值。計(jì)算仍然執(zhí)行但貢獻(xiàn)了錯(cuò)誤的項(xiàng)為了修正它又加了額外的門控邏輯功耗就上去了。解決在映射流程里加一道強(qiáng)制檢查遍歷所有被剪枝的位置確認(rèn)寫入的是零或已被旁路使能覆蓋。上面 4.2 節(jié)的編碼校驗(yàn)?zāi)_本可以直接擴(kuò)展成這一檢查把斷言從“保留位一致”擴(kuò)到“裁剪位為零”。5.3 芯粒數(shù)量一增加算力不線性增長(zhǎng)現(xiàn)象從四顆芯粒擴(kuò)到八顆仿真出來(lái)的算力只有預(yù)期的六成左右增加芯粒帶來(lái)的收益遞減明顯。原因跨芯粒的部分和歸約和激活廣播開始成為瓶頸原來(lái)的切分策略是按四顆設(shè)計(jì)的切分粒度太粗導(dǎo)致每一層都要跨封裝交換中間結(jié)果。互聯(lián)拓?fù)錄]有跟著調(diào)串行點(diǎn)就出現(xiàn)了。解決先做一次互聯(lián)流量統(tǒng)計(jì)找出跨芯粒流量最大的那幾層把切分粒度沿輸出通道方向細(xì)化同時(shí)給每顆芯粒配獨(dú)立激活緩沖把共享緩沖改成點(diǎn)對(duì)點(diǎn)。芯粒規(guī)模繼續(xù)往上時(shí)映射方法本身要重做不能沿用四芯粒的配置。5.4 只按峰值算力做功耗預(yù)算忽略封裝與散熱現(xiàn)象架構(gòu)仿真完美熱仿真一跑結(jié)溫超標(biāo)標(biāo)稱頻率降不下來(lái)實(shí)測(cè)性能只有設(shè)計(jì)值的七成。原因存算一體架構(gòu)的算力密度低但存儲(chǔ)面積大熱量分布很不均勻乘加陣列集中在邊緣SRAM 陣列鋪滿大部分面積峰值算力對(duì)應(yīng)的功耗峰值集中在少數(shù)熱點(diǎn)上。按平均功耗做散熱設(shè)計(jì)熱點(diǎn)必然超標(biāo)。解決把功耗預(yù)算按區(qū)域拆分別給計(jì)算邊緣區(qū)和存儲(chǔ)區(qū)設(shè)定功耗密度上限熱仿真按區(qū)域熱點(diǎn)而不是平均功耗跑。封裝選型時(shí)把熱阻作為與帶寬同級(jí)的指標(biāo)來(lái)評(píng)估。5.5 仿真用理想 SRAM 模型流片后時(shí)序不達(dá)標(biāo)現(xiàn)象RTL 仿真和門級(jí)仿真都過流片回來(lái)頻率上不到目標(biāo)值或者良率異常低。原因存內(nèi)計(jì)算對(duì) SRAM 位單元的讀寫裕度極其敏感行為級(jí)模型里讀操作是零延遲、無(wú)窮驅(qū)動(dòng)實(shí)際位單元在低壓下讀出擺幅很小加上陣列規(guī)模大、走線長(zhǎng)時(shí)序和噪聲裕度都不夠。定制位單元的晶體管數(shù)量、驅(qū)動(dòng)能力、讀出放大器閾值這些參數(shù)在前期模型里常被簡(jiǎn)化。解決從設(shè)計(jì)第一天起就用帶時(shí)序與噪聲的存儲(chǔ)器模型關(guān)鍵路徑上跑蒙特卡洛對(duì)低壓工作點(diǎn)單獨(dú)做讀寫裕度分析把最低工作電壓留出足夠余量。這條問題的成本很高屬于前期少做一步、后期多花幾個(gè)月的那類問題。6. 驗(yàn)收與答辯用一組可復(fù)算的指標(biāo)證明架構(gòu)成立方案交上去評(píng)審?fù)ǔV蛔鋈驴茨愕馁~算不算得清、看你的結(jié)構(gòu)能不能對(duì)得上賬、看你有沒有驗(yàn)證過。所以答辯材料不要從“技術(shù)趨勢(shì)”講起從一張指標(biāo)表講起讓每一個(gè)數(shù)字都能被現(xiàn)場(chǎng)的追問追到底。第一層是帶寬賬。開口就給出目標(biāo)令牌速率下的帶寬需求以及存算一體架構(gòu)下的接口速率兩者放在同一張表里標(biāo)注清楚算法和公式。這一層的作用是確立問題真實(shí)存在并且你的架構(gòu)確實(shí)把它解決了。評(píng)審如果追問“為什么是這個(gè)速率”現(xiàn)場(chǎng)用 2.2 節(jié)的腳本心算一遍比任何解釋都有說服力。第二層是容量與并行度。給出單芯粒容量、宏單元深度、輸出通道并行度三者的關(guān)系式說明在給定面積下為什么選這個(gè)組合。這一層最容易被問的是“為什么不多堆幾顆芯粒”答案要用 5.3 節(jié)的擴(kuò)展性分析來(lái)接承認(rèn)非線性并給出拐點(diǎn)位置比硬說線性更可信。第三層是稀疏協(xié)同的凈收益。把壓縮前后的存儲(chǔ)占用、能效、精度三項(xiàng)并列明確說明壓縮開銷已經(jīng)計(jì)入。用 4.4 節(jié)那張表的形式把生成 QKV 的能效和整體注意力的能效分開報(bào)。第四層是覆蓋面。至少在三個(gè)維度上有驗(yàn)證數(shù)據(jù)功能層編碼解碼一致性、架構(gòu)層映射后的算力與容量、實(shí)現(xiàn)層時(shí)序、面積、功耗。只有功能仿真而沒有實(shí)現(xiàn)層數(shù)據(jù)是這類方案最常見的失分點(diǎn)。驗(yàn)收維度交付物通過判據(jù)帶寬論證公式 復(fù)算腳本 對(duì)比表目標(biāo)速率下接口需求低于封裝能力容量規(guī)劃芯粒預(yù)算表 映射配置權(quán)重全部駐留無(wú)跨芯粒權(quán)重訪問稀疏協(xié)同編碼校驗(yàn)輸出 精度對(duì)照指標(biāo)不降壓縮比達(dá)標(biāo)擴(kuò)展性四芯粒與八芯粒對(duì)照明確拐點(diǎn)與瓶頸位置實(shí)現(xiàn)層時(shí)序、面積、功耗匯總低壓工作點(diǎn)有裕度余量答辯現(xiàn)場(chǎng)還有一個(gè)很實(shí)際的小技巧把最容易出錯(cuò)的兩條5.2 的零權(quán)重未清、5.5 的存儲(chǔ)器模型過理想主動(dòng)寫進(jìn)“已識(shí)別風(fēng)險(xiǎn)”那一頁(yè)附上你的應(yīng)對(duì)措施。評(píng)審看到你自己先把問題擺出來(lái)追問的火力會(huì)明顯下降剩下的時(shí)間就能用來(lái)講你的架構(gòu)亮點(diǎn)。反過來(lái)被現(xiàn)場(chǎng)問出一個(gè)你完全沒想過的坑前面所有數(shù)字的可信度都會(huì)打折。從那以后我每次帶這類硬件架構(gòu)方案都強(qiáng)制走一遍“算賬 → 畫表 → 復(fù)算腳本 → 風(fēng)險(xiǎn)頁(yè)”這四步哪怕時(shí)間再緊也不跳。翻車的方案幾乎都不是因?yàn)橄敕ú粚?duì)而是因?yàn)槟彻P賬只在腦子里算過、沒落到紙上、也沒被第二個(gè)人復(fù)算過。希望這套結(jié)構(gòu)能幫到你把手上那份方案從“看起來(lái)有道理”變成“經(jīng)得起追問”。本文還有配套的精品資源點(diǎn)擊獲取