你有沒有想過,當你把一份十萬字的合同扔給AI助手,讓它幫你審核條款時,那個轉圈的加載動畫背後到底發生了什麼?
答案可能會讓你意外:在這段等待時間裡,顯卡正在做一件極其笨拙的事情。它要把你輸入的每一個字,和前面所有已經輸入過的字,兩兩配對計算一遍相關性。十萬字的文本,配對次數是十萬乘以十萬,也就是一百億次。這還只是一層網路的計算量,大模型往往有幾十層。
這就是所謂的**注意力機制
**
> 注意力機制:大模型理解文本的核心方式,它讓模型在處理每一個字時,都去"回頭看"前面所有字,判斷哪些字和當前字關係密切,從而決定該重點參考誰。
的代價。它讓大模型變得聰明,卻也讓計算量隨著文本長度的增加呈平方級暴漲。文本長度翻一倍,計算量就變成四倍。這個階段有個專門的名字,叫**預填充階段
**
> 預填充階段(Prefilling):大模型處理輸入文本、生成第一個回復字之前的準備計算過程,長文本場景下這個階段往往是最耗時的部分。
騰訊微信團隊
和中科院自動化所
聯合做的這項研究,就是奔著這個"計算量爆炸"的老大難問題去的。他們把這套方案命名為FlashPrefill V2
,而這已經是第二代了。
上一代方案留下的三個坑
故事要從他們的第一代工作FlashPrefill說起。
FlashPrefill的核心思路其實挺聰明的:既然文本里大部分字和字之間的關聯度很低,那為什麼要老老實實地把每一對字都算一遍呢?如果能提前大致猜出哪些區域是"重點關注區",剩下的直接跳過不算,計算量不就降下來了嗎?
這個思路本身沒問題,甚至稱得上巧妙。FlashPrefill用了一種"實時探測"的辦法,先粗略估算出哪些文本塊之間關係密切,再用一套基於最大值的動態閾值規則,快速篩選出真正需要精確計算的部分,跳過了傳統方法裡那種耗時的排序步驟。
但問題是,這套方案離真正能在生產環境裡用起來,還差得遠。
第一個坑是精度失控。當篩選變得越來越激進,也就是說保留的計算量越來越少時,模型的準確率會跟著"雪崩",而且沒有任何剎車機制。你永遠不知道壓縮到什麼程度就會突然崩掉。
第二個坑是引擎太老。FlashPrefill的底層計算核心是建立在FlashAttention-2這套兩年前的技術上的,而如今NVIDIA的Hopper架構
顯卡(比如H20)已經用上了更先進的**FlashAttention-3/4
**
> FlashAttention-3/4:目前最先進的顯卡底層注意力計算加速方案,充分利用了Hopper架構顯卡的異步數據搬運和低精度計算能力,把硬體性能榨得更乾淨。
技術,靠的是TMA(張量內存加速器)和異步流水線這些硬體級的優化手段。用老引擎去跑,就像開著十年前的發動機去跑今天的賽道,再好的路線設計也發揮不出應有的速度。
第三個坑更致命,直接關係到能不能真正落地:FlashPrefill假設所有文本的鍵值緩存是連續存放在顯存里的一整塊。但現代的推理服務框架,比如vLLM
和SGLang
,普遍採用的是**分頁KV緩存
**
> 分頁KV緩存(Paged KV Cache):把顯存像作業系統的虛擬內存一樣,切成一頁一頁管理,不同請求的數據可以靈活分布在不同的顯存頁里,避免浪費空間,這是目前主流推理框架處理並發請求的標準做法。
加**連續批處理**
> 連續批處理(Continuous Batching):讓多個用戶的請求可以動態地混合在同一批計算里處理,一個請求算完了就騰出位置給新請求,而不是死等一批請求全部完成才開始下一批,這樣能大幅提升顯卡的利用率。
的方式來管理顯存,好讓多個用戶的請求可以靈活地混著處理。FlashPrefill那種"整塊連續存放"的假設,根本沒法直接塞進這套體系里。
這就好比你設計了一套非常高效的圖書館檢索算法,前提是所有書都必須按照固定順序擺在一排書架上。可現實中的圖書館為了應對每天成千上萬的借還需求,早就採用了"哪裡有空位就往哪裡塞,用一張索引卡記錄每本書具體在哪"的動態管理方式。你的算法再快,只要它要求書必須連續擺放,就沒法用在這種真實的圖書館裡。如果不解決這個問題,FlashPrefill就永遠只能停留在實驗室里的性能測試,沒法真正服務於線上成千上萬個並發用戶的請求。
FlashPrefill V2要做的,就是把這三個坑一個個填上。
均值修正:給激進的剪枝裝上一道保險絲
先說精度失控這個問題,論文裡給出的解法叫**均值修正**
> 均值修正(Mean Correction):對於那些被判定為"不重要"而被跳過精確計算的文本塊,不是完全丟棄它們,而是用這個塊里所有詞向量的平均值,打個折扣加回到最終結果里,從而挽回被丟棄的那部分概率質量。
要理解這個修正為什麼重要,得先弄明白原來的篩選機制是怎麼工作的。
模型在處理長文本時,會把文本切成一個個小塊,比如每128個字算一塊。對於每一塊,系統會計算出一個"重要性分數",分數達標的塊會被完整、精確地計算,分數不達標的直接扔掉,一點貢獻都不留。
問題出在"扔掉"這個動作上。數學上,softmax注意力機制的輸出是一個加權平均,所有token的貢獻都會累加進最終的分母和分子裡。當你把某些塊直接歸零,相當於人為地扭曲了這個概率分布。當保留的計算量還比較多的時候,丟掉的這部分概率質量占比很小,扭曲可以忽略不計。但當壓縮比例變得極端,比如只保留5%的計算量時,被丟棄的那些塊加起來可能占了相當可觀的概率份額,這時候直接無視它們,誤差就會明顯顯現出來。
均值修正的思路是這樣的:對每一個被判定為不重要、要跳過的文本塊,不是簡單地丟掉,而是先把這個塊里所有的鍵向量和值向量各自求個平均,得到一個"代表向量"。然後用這個代表向量去參與最終的softmax計算,只不過要乘上這個塊里原本有多少個token這個權重,相當於告訴系統"這裡雖然沒細算,但大概有這麼多字,貢獻了大概這麼多概率"。
這個操作背後有一層數學上的巧思。論文用泰勒展開做了詳細推導,證明這種均值替代產生的誤差,本質上只跟塊內部"評分和實際值之間的協方差"有關,是一個二階小量。而如果你什麼都不做直接丟棄,誤差是跟"這個塊和其他塊之間的系統性差異"掛鉤的,是一個更大的量級。換句話說,均值修正巧妙地把一個大誤差換成了一個小誤差。
這就像你去參加一場需要投票表決的會議,有一部分與會者因為各種原因沒法逐條發表詳細意見。如果直接把這些人的意見完全排除在統計之外,最終結果可能會明顯偏離全體真實意願,尤其當缺席的人數比例不小的時候。但如果你至少收集了他們一個大致的"傾向性投票",哪怕不是詳細論證,也能把最終結果的偏差大幅縮小。如果不做這個"大致收集"的動作,壓縮比例一旦超過某個臨界點,模型的表現就會突然失准,你完全沒法預判這個臨界點在哪,這在生產環境裡是不可接受的風險。
論文裡的實驗數據很能說明問題。在RULER這個專門測試長文本能力的評測集上,不加修正的版本在128K長度、FP8精度下,比加了修正的版本要低整整6.2個百分點。而加了修正之後,即便在128K這種極端長度下,跟"完整計算所有內容不做任何壓縮"的版本相比,BF16精度下的差距也只有1.5個百分點左右。更值得關注的是,這個修正操作帶來的額外計算開銷小到可以忽略:在64K序列長度、90%的塊被壓縮掉的情況下,額外延遲也就是3.6到4.5毫秒,相對開銷大概在18%到27%之間,而這恰恰是壓縮最激進、最需要這層保險的場景。
論文還專門做了一個閾值掃描實驗,把控制壓縮激進程度的參數從0.2一路調到0.0125,對應的計算密度從5.2%漲到23.6%。結果發現,加了均值修正的版本,在整個掃描範圍內準確率始終穩定在80分左右浮動,波動不超過0.6分。而沒加修正的版本,同樣的掃描範圍內準確率從74.68一路爬升到78.02,本身就很不穩定,且始終比修正版本低3到5分。這說明均值修正不只是修補了極端情況,而是把整個可用的壓縮區間都拓寬了,原本只能壓縮到9%密度左右保持可用,現在可以壓到5.2%還保持相對穩定。
讓稀疏計算真正"物理提速":跟上硬體的最新節奏
解決完精度問題,接下來要解決的是"算得快不快"這件事。
這裡有個容易被忽視的真相:稀疏計算理論上省了很多次乘法運算,不代表實際跑起來就一定快。如果你的底層代碼寫得不夠貼合硬體特性,理論上省下來的計算量很可能會被各種"隱性摩擦"吃掉,比如內存搬運的延遲、線程之間的等待、指令調度的空轉。
FlashPrefill V2這一部分的工作,本質上是把整套計算核心推倒重來,對齊到目前業界最先進的FlashAttention-3/4的執行範式上。這裡面有幾個關鍵設計,值得逐一拆開看。
第一個是**PackGQA內存布局**
> PackGQA:一種針對分組查詢注意力(GQA)場景的顯存訪問優化技術,通過重新排布查詢矩陣的行列結構,讓多個共享同一組鍵值緩存的查詢頭能夠真正共享顯存加載的數據,而不是各自重複加載一遍。
現在主流大模型為了省顯存,普遍採用**分組查詢注意力**
> 分組查詢注意力(GQA, Grouped-Query Attention):讓多個查詢頭共用同一組鍵值向量,而不是每個查詢頭都配一套獨立的鍵值,這樣能大幅減少儲存鍵值緩存所需的顯存。
的設計,就是讓好幾個"查詢頭"共用同一份"鍵值"數據。這裡有個分組比例g,表示有多少個查詢頭共享一組鍵值。
如果按照最直白的方式來實現,系統會給每一個查詢頭單獨分配一個計算單元,結果就是這g個計算單元會各自重複加載同一份鍵值數據,白白浪費顯存頻寬。PackGQA的做法是把查詢矩陣重新打包,讓一個計算單元里同時裝下這g個頭的查詢,這樣加載一次鍵值數據,g個頭能一起用,加載次數直接除以g。
這就像一個班級里有六個學習小組共用同一本參考書。如果每個小組都各自從書架上取一本相同的參考書回自己座位查閱,那書架的取書壓力就是六倍。但如果把六個小組安排坐在同一張大桌子旁邊,一次性把參考書放在桌子中央,六個小組同時翻看同一本書,取書的動作只需要發生一次。如果不這樣安排座位,顯存頻寬的壓力會隨著分組數量線性增長,尤其在解碼階段查詢長度很短的時候,這種重複加載造成的浪費比例會非常驚人,論文裡提到最壞情況下能省下整整g倍的加載量。
第二個關鍵設計是**warp specialization(warp專精化)**
> Warp專精化:把顯卡上負責計算的線程束(warp)明確分成"生產者"和"消費者"兩種角色,生產者專門負責從顯存搬運數據,消費者專門負責做矩陣乘法運算,兩者並行工作,避免互相等待。
配合**pingpong流水線**
> Pingpong流水線:讓矩陣乘法運算和歸一化計算(softmax)這兩類不同性質的計算任務交替重疊執行,一邊算這一塊數據的乘法,一邊處理上一塊數據的歸一化,讓硬體里不同的計算單元都不閒著。
這兩個設計合在一起,本質上是在打一場"流水線作業"的仗。傳統的同步流水線,是搬運數據、計算、再搬運、再計算,一步一步來,每一步都得等上一步做完。這就好比一家餐館,如果廚師非要等服務生把上一桌的菜全部上完才開始炒下一桌的菜,中間廚房設備大量時間是閒置的。
FlashPrefill V2把線程束明確分工,一部分專門負責從顯存往晶片裡搬運數據(生產者),另一部分專門負責用搬來的數據做矩陣運算(消費者)。生產者和消費者可以同時工作,生產者在搬第n+1塊數據的時候,消費者正在處理第n塊數據的計算,兩者互不阻塞。更進一步,在消費者內部,矩陣乘法和歸一化運算(也就是softmax里的指數運算和重新縮放)也做成交替重疊的節奏,一邊算這塊的QK矩陣乘法,一邊算上一塊的PV矩陣乘法,一邊處理更上一塊的歸一化,三件事情齊頭並進。如果不這樣設計,硬體里負責矩陣乘法的核心單元和負責普通運算的核心單元就會互相干等,誰也不能真正把對方的空閒時間利用起來。
第三個是**FP8**
> FP8:一種比常見的BF16精度更低、數據體積更小的浮點數表示方式,用更少的比特位儲存數字,計算速度更快、顯存占用更少,但精度損失需要額外補償。
支持。這是這次升級里專門為了配合實際部署需求加的能力。FP8的挑戰在於,它的數值動態範圍比BF16窄得多,直接拿來做softmax容易溢出或者精度丟失。論文裡用了一個巧妙的技巧:把softmax里的概率值先乘以256這個偏移量,把數值映射到e4m3格式能表示的完整動態範圍里,這個偏移量在最後計算比值的時候會自動抵消掉,不會影響最終結果,只在做log-sum-exp匯總的時候才需要專門處理一下。
論文還提到了一個很細節的工程難題:FP8下做矩陣乘法的第二步(也就是概率乘以值向量)時,硬體要求數據必須按照特定的轉置格式排列,但值向量原始儲存可能是按行存的,也可能是按列存的,兩種情況需要不同的處理方式。列存的話,直接在寄存器里用位移和字節重排來調整概率矩陣的格式,不產生額外的顯存讀寫;行存的話,則要在共享內存里做一次轉置操作,過程中還得小心避開"儲存體衝突"這種硬體層面的性能陷阱。這些細節聽起來瑣碎,但正是這些看似不起眼的工程打磨,決定了理論加速比能不能真正兌現成顯示器上看到的秒表讀數。
這三項設計疊加起來,效果非常顯著。在H20顯卡上,128K上下文長度下,FlashPrefill V2相比FlashAttention-2實現了27.19倍的加速(BF16精度),FP8版本更是達到47.26倍。就算拿來跟同樣用了先進流水線技術的密集計算基準比,FlashPrefill V2依然能快17.54倍(BF16)和30.49倍(FP8)。而且這個加速優勢不是只有極長文本才有,4K這種相對短的文本長度下,依然能跑贏老版本的FA2。
讓稀疏注意力真正能塞進生產系統里
前兩項工作解決的是"算得准"和"算得快",第三項工作解決的是"能不能真的用起來"。
前面提到過,現代推理服務框架普遍用分頁KV緩存和連續批處理來管理顯存和調度請求。FlashPrefill V2這次專門針對這套體系做了原生適配。
核心機制是一個叫**CSR索引**
> CSR索引(Compressed Sparse Row):一種緊湊記錄"哪些塊被選中"的數據結構,只儲存被選中塊的編號列表,而不是給每個塊都標記一個"選中或不選中"的標誌位,這樣在選中比例很低的時候能大幅節省索引本身占用的儲存空間。
的數據結構。它的思路很簡單:與其給每一個文本塊都打一個"選中/不選中"的標記(哪怕這個塊根本沒被選中,這個標記位也占地方),不如只記錄那些真正被選中的塊的編號列表。當選中比例很低的時候,比如只有5%的塊被選中,這種記錄方式能省下大量的索引儲存空間。
論文裡做了個對比:跟騰訊內部另一套生產級的稀疏注意力算子HPC-Ops相比,在128K長度、單批次32個查詢頭4個鍵值頭的配置下,HPC-Ops採用的是密集掩碼格式,不管實際密度多少,索引占用固定是32MB。而FlashPrefill V2用CSR索引,在5%密度下只占用6MB,省了超過80%的索引儲存開銷。
分頁KV緩存這塊,論文裡給出了一個具體的地址計算公式:某個鍵值token的真實顯存地址,等於查頁表得到這一頁的起始地址,再加上這個token在頁內的偏移量。這個查表過程通過異步拷貝指令在後台完成,不阻塞主計算流程。
調度層面還有一個值得一提的細節,叫**KV分裂負載均衡**
> KV分裂負載均衡:在稀疏場景下,把每個查詢塊實際選中的鍵值塊數量作為衡量計算量的標準,而不是簡單按位置範圍切分工作,確保多個並行處理單元分到的實際工作量是均等的,避免某些單元乾等著別的單元把活幹完。
因為稀疏計算下,不同的查詢塊需要處理的實際計算量差異很大,有的塊可能選中了一大堆鍵值塊要算,有的可能只選中了寥寥幾個。如果還是按照位置範圍機械地切分給不同的並行單元,就會出現有的單元早早算完在那兒閒著,有的單元還在埋頭苦幹的情況。FlashPrefill V2改成按"實際選中的塊數量"來切分工作,確保每個並行單元分到的活兒是均等的。
這些工程細節堆疊起來的最終效果,體現在SGLang這套真實的推理服務框架里的表現上。團隊把FlashPrefill V2作為標準的注意力後端集成進SGLang,無需改動模型定義、KV緩存結構或調度邏輯。在128K上下文、批次大小16的場景下,Qwen3-30B-A3B模型的首字延遲(也就是用戶從提交請求到看到第一個字蹦出來的等待時間)從123.2秒壓縮到36.2秒(BF16),FP8精度下更是壓到了25.5秒。
在更貼近真實場景的開放式並發測試中,也就是模擬用戶按照泊松分布隨機到達、請求長度混雜在4K到128K之間的場景下,效果更加突出。傳統的密集計算後端在16次每秒的請求速率下幾乎被壓垮,首字延遲的中位數長達82到106秒,請求吞吐能力被死死卡在每秒0.29到0.37個請求。而FlashPrefill V2把首字延遲中位數壓到17到46秒,請求吞吐提升到每秒0.70到0.76個,FP8版本更進一步提升到每秒0.88到1.34個。有意思的是,這種加速在低負載下反而更明顯,因為長文本的預填充過程會一直占用計算資源,進而拖慢排在後面等待解碼的其他用戶請求,預填充算得越快,整個系統的排隊積壓就消解得越快。
論文還專門測了一下跟**分塊預填充**
> 分塊預填充(Chunked Prefill):為了保護並發請求的響應延遲,把一個長文本請求的計算拆成若干個小塊分批處理,而不是一次性把整個長文本算完再讓出計算資源給別的請求,這是生產系統里常用的延遲保護機制。
配合使用的情況。分塊本身會稀釋稀疏計算的優勢,因為索引選擇這個動作要在每個小塊上重新做一遍,而且每個短塊里那些必須保留的"錨點""窗口"區域占比會相對變高,拉高了整體密度。測試顯示,把分塊大小設為8K時,加速效果會打一些折扣,但設為16K時基本能恢復大部分優勢,所以論文建議實際部署時分塊大小至少設為8K。
數據說話:三個模型、兩大評測集的全面驗證
這套方案不是紙上談兵,團隊在Llama-3.1-8B-Instruct、Qwen3-4B-Instruct-2507和Qwen3-30B-A3B-Instruct-2507三個具有代表性的模型上,用RULER和LongBench兩個長文本評測基準做了系統驗證。
| 模型 | 完整注意力平均分 | FlashPrefill V2平均分 | FlashPrefill V2-FP8平均分 |
|---|---|---|---|
| Llama-3.1-8B-Instruct | 88.82 | **87.79** | 86.57 |
| Qwen3-4B-Instruct-2507 | 87.06 | **86.23** | 85.82 |
| Qwen3-30B-A3B-Instruct-2507 | 92.05 | **91.76** | 91.39 |
從這張表能看出,FlashPrefill V2在三個模型上都能把精度損失控制在1到1.3分以內,FP8版本的額外損失也只有零點幾分。而在LongBench這個覆蓋更廣泛真實任務的評測集上,FlashPrefill V2在三個模型上的平均分都超過了此前所有的對比方法,比如在Llama-3.1-8B上拿到49.31分,比排名第二的XAttention(48.25分)還高出一截。
速度對比方面,下面這張表匯總了不同方法在128K上下文長度下相對FlashAttention-2的加速倍數:
| 方法 | 128K加速倍數 |
|---|---|
| MInference | 2.76× |
| FlexPrefill | 5.43× |
| XAttention | 3.42× |
| FlashPrefill(第一代) | 18.67× |
| FlashPrefill V2 | 27.19× |
| **FlashPrefill V2-FP8** | **47.26×** |
從2.76倍到47.26倍,這中間差了十幾倍的鴻溝,背後就是這篇論文裡講的三層升級:更聰明的壓縮策略、更貼合硬體的計算核心、更適配真實部署環境的系統設計。三者缺一不可,少了均值修正,壓縮激進不敢用;少了硬體對齊,理論加速兌現不了;少了分頁緩存適配,方案壓根用不進生產系統。
寫在後面
讀完這篇論文,最讓我意外的其實不是速度提升了多少倍,而是團隊願意花這麼大的篇幅去講工程細節。FP8下概率矩陣怎麼用位移和字節重排在寄存器里轉置,避免儲存體衝突要怎麼排布列的順序,這些內容放在一篇論文裡其實挺少見的,大部分做算法的論文不會寫到這個粒度。這說明這個團隊清楚地知道,稀疏注意力這條路走了好幾年,卡脖子的從來不是"想不想得到壓縮的點子",而是"這個點子能不能在真實硬體上兌現成秒表上的數字"。
還有一個細節值得單獨說一說:均值修正在FP8下的重要性明顯高於BF16。這背後的原因論文解釋得挺有意思,是因為量化壓縮了分數之間的差距,導致原本該被判定為"不重要"的塊,實際承載的概率份額反而變大了。這提醒我們,低精度和稀疏化這兩件事疊加在一起時,誤差不是簡單相加,而是會互相放大,這在設計其他壓縮方案時可能也是個容易被忽視的坑。
如果長文本處理的成本能一直這樣指數級下降,下一步大概率會有人開始琢磨反過來的問題:既然預填充這麼快了,能不能幹脆讓上下文長度不設上限,把整個知識庫直接塞進提示詞裡,不再需要檢索增強這類折中方案?這個問題現在還沒有答案。
Q&A
Q1:FlashPrefill V2是什麼?
A:FlashPrefill V2是騰訊微信團隊聯合中科院自動化所提出的一種長文本大模型推理加速方案,專門針對處理長文本時的預填充階段做優化,通過稀疏注意力計算、均值修正和硬體級核心重寫,在128K上下文長度下實現最高47.26倍的加速。
Q2:FlashPrefill V2相比第一代FlashPrefill改進了什麼?
A:主要有三點改進。一是引入均值修正機制,解決了極端壓縮下精度崩潰的問題;二是把計算核心重寫對齊到最新的FlashAttention-3/4架構,充分利用Hopper顯卡的硬體特性並支持FP8低精度計算;三是原生支持分頁KV緩存和連續批處理,能直接集成進SGLang這類主流推理框架。
Q3:FlashPrefill V2會不會明顯損失模型的回答質量?
A:不會。在RULER和LongBench兩個長文本評測集上,FlashPrefill V2的平均得分和完整注意力計算相比只差1到1.3分左右,即使在128K這種極端長度、只保留不到5%計算量的情況下,差距也控制在1.8分以內。






