在 Apple Silicon 上最佳化裝置端推論
改善預填充與解碼吞吐量的自定義本地引擎。
Apple 晶片上的混合運算協調了雲端中的前沿智慧與 Mac 上的本地模型之間的任務。雲端模型處理研究與推理,而本地模型則處理 Mac 上的私密檔案與應用程式。
為了讓這種分工感覺順暢,本地推論必須跟上任務其餘部分的步調。這需要一個能夠快速處理提示並維持高 Token 生成率的引擎。
Lily 是我們針對 Apple 晶片與 Qwen3.6-35B-A3B 專門建構的輕量級本機推論引擎,並針對預填與解碼進行了獨立最佳化。獨立展示版已在 GitHub 上公開提供。
簡介
在 Mac 上執行 LLM 的常見方法是使用 MLX(Apple 針對 Apple 晶片推出的開源機器學習框架)。其附屬函式庫 MLX-LM 新增了載入及使用各種語言模型生成文字所需的元件。MLX 與 MLX-LM 結合,為本地 LLM 推論提供了現成的通用堆疊。
Qwen3.6-35B-A3B 是一個稀疏混合模型:它使用混合專家(MoE)路由,並將固定大小的遞迴狀態與全注意力機制結合。這些架構選擇減少了所需的運算量,但也造成了不規則的工作負載。Token 會路由至不同的專家權重,且遞迴狀態本質上是循序的。
MLX-LM 已經會針對推論階段與常見的工作負載形狀選取優化核心,但其可重複使用的運算必須支援許多模型架構。專門針對 Qwen 設計的引擎能夠在模型與執行時期層級進行專門化,圍繞著模型的固定結構來協調核心、資料移動以及排程。
Lily 在單一處理器中端對端地實作了此專門化。Rust 執行時期會載入模型檢查點並管理工作階段狀態與生成循環,相容於 OpenAI 的聊天完成 API 接受請求並串流傳輸 Token,而自定義 Metal 核心則執行 Qwen 專屬的運算。執行路徑中既沒有 PyTorch也沒有 MLX。
我們分開測量預填充與解碼效能。預填充吞吐量捕捉引擎處理提示的速度;解碼吞吐量則捕捉其生成輸出 Token 的速度。
我們在一台配備 M5 Max、40 核心 GPU 與 128 GB 統一記憶體的單一 MacBook Pro 上,對 Qwen3.6-35B-A3B 進行基準測試。在用於 prefill 的十種提示長度與用於 decode 的十種上下文長度中,從 256 到 128K tokens(K = 1,024),該引擎的 prefill 吞吐量平均為 MLX-LM 的 1.22 倍,decode 吞吐量平均為其 1.32 倍。在 4K-token 提示與 4K-token decode 上下文下,自訂引擎達到每秒 5,749.9 個 prefill tokens 與每秒 186.6 個 decode tokens;相較之下,MLX-LM 分別為 4,737.5 與 140.9。在多輪工作階段中,這些時間節省會隨每次額外的模型呼叫而累積。
接下來,我們將說明 Qwen 的架構如何在 Apple 晶片上創造出模型專屬的優化機會。接著我們將逐步解說隨之而來的預填充與解碼變更。我們也會涵蓋額外優化停止帶來效益的時機,最後以與 MLX-LM 的端對端比較作為結尾。
Apple 晶片上針對 Qwen 的優化機會
Qwen 創造了三種不同的工作負載形狀
Qwen3.6-35B-A3B 包含 3,500 億參數,但每個 Token 僅會啟動大約 30 億個。路由器會對 256 個專家子網路進行評分並選取八個,同時搭配一個處理每個 Token 的共用專家。這種稀疏 MoE 設計減少了運算,但也產生了不均勻的工作:專家接收到不同數量的 Token,且每個 Token 都需要來自不同專家組合的權重。
Qwen 同時結合了 10 個全注意力層與 30 個 Gated DeltaNet 層。這兩種層類型以不同的方式保留先前的資訊。
注意力層使用群組查詢注意力(GQA)。Qwen 擁有 16 個查詢頭與兩個金鑰值(KV)頭,由八個查詢頭共用每個 KV 頭。共用可使 KV 快取更小,並允許在查詢頭之間重複使用快取的資料。快取仍然會為每個 Token 儲存新的金鑰與值,因此隨著上下文增長,每個解碼步驟都會讀取更多資料。
相反地,Gated DeltaNet 會將先前的資訊壓縮成固定大小的遞迴狀態。學習到的閘門會控制要保留多少現有狀態,而 delta 更新則會納入來自目前 Token 的資訊。模型以遞迴方式定義這些更新,因此每個 Token 都取決於前一個 Token 所產生的狀態。然而,在預填充期間,引擎可以透過兩種方式評估相同的運算。它可以直接掃描 Token 同時向前傳遞狀態,或是將更新重新組織成能暴露出更多矩陣運算與 Token 層級平行處理的區塊。哪種方法較快取決於模型維度、工作負載與硬體。
總結來說,這些結構會產生三種運算模式:不均勻的專家群組、針對增長中快取執行的注意力機制,以及可直接或以區塊進行評估的固定大小遞迴。
Apple 晶片針對不同的工作負載提供不同的路徑
預填充會一次處理許多提示 Token 活化列。此處考量的本地工作負載通常一次解碼一個請求(批次 1)並在每一步處理一個新列。這種差異改變了相同模型權重的使用方式。預填充可以在數百或數千列中重複使用每個權重區塊。解碼則大致無法如此,因為每個新 Token 都需要再次通過權重。
Apple 晶片將 CPU 與 GPU 置於統一記憶體(雙方皆可存取的單一實體記憶體池)後方。這允許模型在無需維護獨立 GPU 複本的情況下保持常駐,但這並不會使資料移動變得免費。讀取權重與中間值仍然會消耗記憶體頻寬,而暫存器與其他晶片上儲存空間則較快但小得多。
M5 GPU 也提供不同的運算路徑。預填充的線性層使用一般矩陣乘法(GEMM),將權重矩陣同時套用到許多列。相容的 GEMM 可以透過 Metal 4 張量運算使用每個 GPU 核心中的神經加速器。相對地,批次 1 解碼使用一般矩陣向量乘法(GEMV),將相同的權重套用到單一列。由於權重重複使用率低,GEMV 主要受限於記憶體頻寬,因此比專為具有更高資料重複使用的矩陣運算而設計的神經加速器更適合 GPU 的向量算術邏輯單元(ALU)。
這些執行路徑並非 Lily 獨有。MLX 在相同的統一記憶體上運作,並根據工作負載形狀選取優化的矩陣與向量核心。MLX-LM 的 Qwen 實作 已經將專家工作分組,以融合的遞迴 Metal 核心評估 Gated DeltaNet,並使用感知 GQA 的注意力機制。這些功能是 Apple 晶片上進行高效 Qwen 推論的共用起點。
優化策略
Lily 較窄的範疇使其能夠繞過 Qwen 精確的架構與維度來協調這些共用的執行路徑。它使用階段特定的 GPU 路徑,對應 Qwen 的專家、遞迴與注意力工作負載以將資料移動減到最少,並從測量的工作負載形狀中選取核心與配置。該策略包含三個部分:
- 將 GPU 路徑與推論階段相匹配。 當預填充可以在許多列之間重複使用權重時,使用以矩陣為導向的執行;當批次 1 解碼一次處理一列時,則使用以向量為導向的執行。
- 將 Qwen 的結構對應至 GPU 上,同時將資料移動減到最少。 保持權重壓縮直到使用時為止,組織路由的專家工作而無需返回 CPU,在遞迴掃描期間將 Gated DeltaNet 狀態保留在晶片上,並重複使用由群組查詢注意力共用的 KV 資料。
- 調整核心以適應工作負載形狀。 在每個階段中,從可用的列數、列在專家間的分佈、運算的維度以及目前的上下文長度中,選取磚塊大小、執行配置與注意力路徑。
以下章節將說明這些選擇。對於在 M5 Max 上透過匹配消融實驗評估的優化,我們透過比較僅在所研究的優化上有差異且其他方面完全相同的引擎配置,來評估其影響。由於這些實驗是將引擎的版本與自身進行比較,因此它們解釋的是機制,而非將最終結果相對於 MLX-LM 進行分解。
預填充:重複使用權重並將路由保留在 GPU 上
預填充會一次暴露出許多 Token 列,但 Qwen 會將這些列不均勻地路由至各個專家,並透過序列更新遞迴狀態。其優化分為三組:圍繞著路由列組織稀疏專家工作、將 Gated DeltaNet 掃描保留在晶片上,以及將長提示分割成有界的區段。
優化稀疏專家運算
在矩陣乘法期間進行權重反量化
Qwen3.6-35B-A3B 檢查點使用群組式仿射 4 位元量化。每個權重皆儲存為 4 位元整數代碼,而每組 64 個權重則共用一個用於重建其值的 bfloat16 比例與偏差。這將 3,500 億參數模型從大約 70 GB 的 bfloat16 權重縮減至 19.4 GB 的檢查點,使得將模型常駐在 Mac 上變得切實可行。
用於矩陣乘法的 Metal 4 張量運算消耗的是 bfloat16運算元,而非打包的 4 位元表示法。在乘法之前,GPU 必須以 bfloat16 重建權重。Lily 中優化的群組 GEMM 一次只對一個小型權重磚塊執行此轉換,並將結果保留在晶片上的執行緒群組記憶體中,時間剛好足夠将其乘以路由的活化列。累積運算使用 32 位元浮點數,輸出則以 bfloat16 寫入。完整的展開權重陣列絕對不會在統一記憶體中建立。
在消融實驗中,反量化是作為獨立運算執行的:它將 4 位元權重展開為統一記憶體中的 bfloat16 陣列,之後矩陣核心再將該陣列讀回。在 512 Token 提示下,將反量化移至群組 GEMM 中透過消除此次中間寫入與讀取,使端對端預填充吞吐量提升了 77.4%。
將專家路由保留在 GPU 上
群組 GEMM 需要將指定給每個專家的活化列儲存在一起。在為每個 Token 選取八個專家之後,直方圖會計算有多少分配給了每個專家。前綴掃描會將這些計數轉換為起始位移,分散步驟會將列放入其專家群組中,而區塊對應則會列出群組 GEMM 必須處理的固定大小矩陣區塊。
經過優化的路徑會將每個提示區段的這個完整序列保留在單一命令緩衝區(一個排序的 GPU 運算批次)中。相對地,消融實驗會暫停,以便 CPU 檢查路由中間值並提交下一個運算。將直方圖與前綴掃描保留在 GPU 上會增加兩個核心,但消除了每個 MoE 層內部 CPU 與 GPU 之間的同步。
在 512 Token 提示下,啟用 GPU 常駐路由使端對端預填充提升了 89%。這也顯示了為什麼單純的核心數量可能會具有誤導性:較快的路徑會啟動較多的核心,但在層內部絕不會等待 CPU。
將磚塊大小與專家負載相匹配
在 2K Token 提示下,將每個 Token 路由至 256 個專家中的八個會產生 16,384 個 Token 與專家的配對,平均每個專家有 64 個活化列。實際分佈並不均勻:某些專家接收到許多列,而其他專家則接收很少。
群組 GEMM 將每個專家的輸出劃分為磚塊,這些是矩陣乘法輸出的小型矩陣長方形區塊。每個磚塊都會指定給一個 GPU 執行緒群組。在 Apple 晶片的 GPU 上,執行緒群組包含一個或多個 simdgroups,每個 simdgroup 由 32 個以鎖步(lockstep)執行指令的討論串組成。
較大的磚塊(tile)可將設定成本分散到更多列中,並暴露出更多平行工作,但當某個專家僅接收到少數幾列時,大型磚塊的一部分仍會保持閒置。因此,磚塊大小與 simdgroup 數量是相輔相成的。
消融實驗將磚塊固定為 16 列。以此控制組為基準,在 2K Token 下啟用具有四個 simdgroups 的 32 列磚塊使端對端預填充提升了 13.2%。
將遞迴狀態保留在晶片上
在預填充期間,每個 Gated DeltaNet層會依序掃描提示,同時向前傳遞其遞迴狀態。若停用暫存器常駐,消融實驗會使用區塊式掃描。在 2K Token 提示下,該路徑每層會移動 256 MiB(百萬位元組)的狀態,並反覆在屏障(所有參與討論串必須互相等待的同步點)處停止合作討論串。
遞迴狀態是一個矩陣。經過優化的核心會將每一欄指定給一個 simdgroup。該 simdgroup 將該欄分配到其各個討論串中,將該欄載入其暫存器中一次,並在整個掃描過程中持續傳遞該狀態。討論串之間透過 simdgroup 運算來交換中間結果,而不是透過跨討論串共用的晶片上儲存空間(即執行緒群組記憶體)。完成的狀態只會在掃描結束後才寫回。
狀態及其閘門使用 32 位元浮點格式,因為微小的捨入誤差會在循序更新中不斷累積。查詢與金鑰活化保持在 bfloat16 格式。
在 2K Token 提示下,啟用暫存器常駐掃描使端對端預填充提升了 5.6%。專家 GEMM 佔了約 90% 的預填充時間。循序掃描無法暴露出足夠的可重複使用矩陣工作,因此無法受惠於神經加速器。
透過提示分塊來限制暫時記憶體
執行時期會將長提示處理為一系列有界的區段,而不是同時將每個提示 Token 的暫時資料保留在記憶體中。模型權重會持續常駐在統一記憶體中,而遞迴狀態與 KV 快取則將上下文從一個區段傳遞到下一個區段。不會捨棄任何先前的上下文。
若無分塊,暫時活化陣列會隨著完整提示而增長,並與模型權重、遞迴狀態以及 KV 快取競爭統一記憶體。分塊每次僅保持一個片段的暫時值處於活動狀態,然後在處理下一個片段之前釋放或重複使用該儲存空間。這限制了尖峰工作記憶體,使引擎能夠處理更長的提示而不會改變模型的輸出。
分塊預填充在許多引擎中很受歡迎,對於在這些記憶體受限的環境中服務長多輪軌跡至關重要。注意力層的總預填充時間對提示長度保持二次方關係,並伴隨來自先前區段重複 KV 載入的一些額外負擔。
解碼:將每個 Token 移動的位元組減至最少
批次 1 解碼每次處理一個新列。由於權重重複使用率低,其吞吐量主要取決於引擎為每個 Token 移動了多少位元組。解碼的變更分為四組:優化單列權重路徑、將每個步驟保留在 GPU 上、減少中間與狀態流量,以及有效率地讀取注意力快取。
優化單列權重路徑
MLX 已經將單列工作分派給專門的矩陣向量核心。由於 Lily 不使用 MLX,自定義執行時期必須提供相同的基本策略。我們的列平行 GEMV 專為單一活化列而設計。一個 simdgroup 會在輸出上進行合作,同時平行讀取權重矩陣的不同部分。
將每個解碼步驟保留在 GPU 上
將 Token 交接保留在 GPU 上
每個解碼步驟都以選擇下一個 Token 作為結束;隨後的步驟則以該 Token 作為輸入開始。將選擇傳送至 CPU 然後再傳回 GPU 會為每個 Token 增加一個同步點。相反地,我們的執行時期會在兩個命令緩衝區與兩個 GPU 常駐 Token 插槽之間交替。GPU 會選擇最高得分的 Token,並直接將其 Token ID 寫入下一個解碼步驟的輸入插槽中,同時 CPU 則準備後續的工作。
重疊獨立的 GPU 工作
在一個記錄的批次 1 解碼步驟中,生成一個 Token 啟動了 795 個 GPU 核心。它們的相依性形成了 555 個循序階段,使某些核心能夠自由地同時執行。然而,Metal 的序列執行模式仍然依序執行了每一個核心。
經過優化的解碼路徑會在並發的 Metal 傳遞中記錄實際的資料相依性。當 GPU 資源允許時,獨立的核心啟動可以同時執行。只有在後續工作需要先前結果時才會插入屏障。
減少中間與狀態流量
獨立的核心通常會具現化一個中間值:一個核心將暫時結果寫入記憶體,下一個核心則將結果讀回。經過優化的解碼路徑融合了四個鏈結:兩個專家輸入投影及其閘門活化;專家輸出投影及其路由分數與共用專家結果;注意力之前的查詢與金鑰準備;以及帶有標準化的遞迴更新。每個融合的核心會將暫時值保留在暫存器中,而不是透過記憶體傳送。
融合也縮短了相依性圖表:當中間寫入消失時,保護其消費者的屏障也隨之消失。
有效率地讀取注意力快取
合併注意力快取讀取
注意力機制會在每個解碼步驟期間從 KV 快取讀取金鑰與值。在消融實驗中,相鄰的 GPU 討論串並不總是請求相鄰的位元組,這迫使記憶體系統必須提供更多獨立的事務。啟用合併載入可使相鄰討論串請求相鄰位元組,以便硬體可以合併其讀取。
在 bfloat16 配置下,合併(coalescing)將金鑰頻寬從 33.8 GB/s 提升至 47.9 GB/s、將數值頻寬從 42.0 GB/s 提升至 61.8 GB/s,並在 3,840 個 Token 的上下文中將端對端解碼提升了 2.1%。
打包查詢頭以重複使用 KV 列
群組查詢注意力機制允許八個查詢頭共用一個 KV 頭。在消融實驗中,每個查詢頭都在獨立的 simdgroup 中運行,因此所有八個查詢頭都會各自獨立請求相同的快取 KV 列。經過優化的核心將四個查詢頭打包進一個執行緒群組中,該執行緒群組會載入每個 KV 列一次,並在四項注意力計算中重複使用。第二個執行緒群組則負責處理其餘的四個頭。
這項通常稱為 GQA 打包的技術執行相同的算術並產生相同的輸出位元組,同時將八個獨立的 KV 請求減少為兩個共用載入。相對於未打包的消融實驗,它在 32K Token 上下文中將端對端解碼吞吐量提升了 23.8%。
在長上下文切換注意力配置
全注意力層中的每個解碼步驟都會掃描現有的 KV 快取。固定區塊配置會將該快取劃分為相等的片段,供 GPU 進行平行處理。當快取較小時,額外的排程並不划算,但隨著上下文增長,固定區塊配置能更均勻地平衡工作負載。
針對此模型,執行時期將一般注意力路徑保持在 32K Token 以下,並在 32K 或更長時使用固定區塊路徑。當每個頭有 256 個值且八個查詢頭共用一個 KV 頭時,就會套用此切換;其他形狀則保持在一般路徑上。消融實驗會停用此切換並一律使用一般路徑。在 32K 時,啟用固定區塊路由使端對端解碼提升了 7.7%,在 64K 時提升了 27.4%,在 128K 時則提升了 40.2%。
進一步優化的限制
某些變更改善了獨立的運算,但並未改善端對端推論。
推測式解碼(speculative decoding)(使用較小的模型來提出 Token 供完整模型驗證)使批次 1 解碼變慢了 18%。驗證處理的是兩到五列的群組,這對此硬體而言是不具效率的形狀,且這些列經常選取不同的專家,從而增加了讀取的專家權重資料量。減少草擬器的輸出詞彙量使草擬器吞吐量提升了 4.7–5.1%,但並未使完整的推測式循環變快。此結果具有工作負載專屬性:我們在 Blackwell 上的批次 Qwen 部署會在不同的條件下使用推測式解碼。
其他實驗包括減少 GPU 啟動、重疊整個階段、使用較大的預填充磚塊、套用更廣泛的融合、加速路由器,以及將輸出投影與 Token 選擇結合。沒有一項能改善完整的推論循環。
硬體極限的測量也顯示,主要的預填充與解碼運算中僅剩微幅的餘裕。MoE GEMM 和 GEMV 達到了其存取模式最快持續權重讀取速率的 97.9% 和 90.3%。從稀疏 GEMV 中移除算術運算僅使吞吐量改變了 0.2%,這證實了權重讀取而非運算才是限制資源。類似地,預填充的矩陣乘法在獨立情況下達到了理論矩陣極限的 93%,而在測試模型內部則達到了 80–86%。
端對端效能
端對端比較會在兩個引擎中載入相同的 4 位元檢查點位元組,並在單一 40 核心、128 GB M5 Max 上一次執行一個請求。在每一輪中,兩個引擎會以交替順序執行,以減少背景負載與晶片溫度變化所帶來的偏差。我們與 MLX-LM 最快的直接生成路徑進行比較,而非其伺服器,因此測量重點在於模型執行而非服務額外負擔。
此掃描涵蓋從 256 到 128K 個 Token 的預填充十個提示長度與解碼十個上下文長度。隨著引擎將固定的設定成本分散到更多 Token 中,預填充吞吐量最初會上升。預填充在大約 4K Token 提示時達到高峰,接著因為十個全注意力層隨著提示增長而執行更多工作而下降。解碼在短上下文中幾乎保持平坦,一旦讀取增長的 KV 快取變得顯著時便會下降。自定義引擎在每個記錄的長度上都更快。
由於專門化的執行可能會改變浮點運算的順序,我們也對照 MLX-LM 檢查了數值的一致性。在教師強制(teacher-forced)比較中,兩個引擎在 192 個位置中的每一個位置都從相同的參考前綴預測下一個 Token,從而防止先前的差異影響後續的輸入。Lily 的困惑度(perplexity)僅高出 0.04%,並且在 96.35% 的受測位置中選取了相同的最高排名 Token。
為本地平台打造
Apple 晶片並非較小的資料中心 GPU。它是一個完整的本地推論平台,擁有自身的硬體與軟體特性。統一記憶體為單一節點提供了極高的上限,使其能容納大量的模型與狀態。M5 神經加速器吸收了預填充中的密集矩陣工作。向量 ALU 則處理解碼中受限於頻寬且低重複使用的剩餘部分。
Qwen 為專門化帶來了進一步的機會:將專家路由與遞迴狀態保留在 GPU 上、消除不必要的中間值、重疊獨立工作、重複使用共用的 KV 資料,以及調整核心以適應工作負載形狀。
透過針對模型與平台的優化,單台 Mac 即可高效執行大型稀疏模型。未來的研究將擴大對不同模型、晶片與服務工作負載的涵蓋範圍,並將此處在單一配置上驗證的機制轉化為更通用的執行時期政策。
更廣泛的原則是讓引擎同時匹配模型的架構以及硬體特定的運算與記憶體路徑。隨著前沿開源權重模型與硬體的演進,高效能本地推論將日益依賴專為兩者量身打造的引擎,而非抽象化其差異的引擎。
引用
@online{perplexity2026OptimizingOnDeviceInferenceForAppleSilicon,
author = {{Perplexity} and Yibo Wu and Lequn Chen and Kevin Hu and Phi Hoang and Claire Beamer and Jeremy Yang and Denis Yarats},
title = {{Optimizing on-device inference for Apple Silicon}},
date = {2026-09-01},
url = {https://www.perplexity.ai/hub/blog/optimizing-on-device-inference-for-apple-silicon}
}