Suy luận MoE · Một máy

Stream một mô hình Mixture-of-Experts 35B trong 4 GiB RAM

streamoe, một engine suy luận MoE công khai bằng Rust có giới hạn bộ nhớ: Qwen3.6-35B-A3B trả lời câu hỏi văn bản và hình ảnh trong giới hạn 4 GiB trên laptop M3 Pro ở 7,4 tok/s, stream expert từ SSD qua một hot tier. Kiến trúc AEGIS, số đo, và phép tính giải thích chúng.

Tran Sy Bao (Johnny) · TP. Hồ Chí Minh · · 20 phút đọc

RAM · giới hạn 4 GiB, lập kế hoạch sẵnTrọng số1,29 GiB, ngoài expertKV cache0,16 GiBHot tier1,05 GiB expertSSD trong16,9 GiB expert~4,4 GB/s, đọc 1,7 MiBmiss + prefetchGPU · Metal8 trên 256 expert / lớpToken ra7,4 tok/s ở 4 GiB

một token: expert đã có trong hot tier được dùng tại chỗ; phần còn lại stream từ SSD, và không gì vượt khỏi kế hoạch 4 GiB

Con số chính

4 GiB
giới hạn RAM cho một mô hình 35B tham số
3,74 GiB
bộ nhớ đỉnh đo được dưới giới hạn đó
7,4 tok/s
decode ở giới hạn 4 GiB; 8,1 với 3-bit đóng gói
≈ 138 ms
mỗi token: 61 GPU, 50 chờ SSD, 27 CPU
Trong bài này

    Tóm tắt

    Phiên bản ngắn

    • Mục tiêu: kích thước mô hình không nên quyết định máy nào chạy được nó; lượng bộ nhớ cho phép mới nên quyết định. Bạn đặt một giới hạn, engine lên kế hoạch cho từng byte bên trong và giữ đúng kế hoạch đó.
    • Kết quả: Qwen3.6-35B-A3B, một mô hình Mixture-of-Experts (MoE) thị giác–ngôn ngữ 35B tham số, nặng 19 GiB ở 4-bit, trả lời câu hỏi văn bản và hình ảnh trong giới hạn 4 GiB trên một laptop M3 Pro dùng chung, đỉnh 3,74 GiB. Decode đạt 7,4 tok/s trên ba prompt chat, 8,1 với expert 3-bit đóng gói, và 6,5–9,2 tok/s với giới hạn từ 3 đến 6 GiB. Với expert 4-bit, đầu ra greedy trùng với bản tham chiếu MLX.
    • Thiết kế: kiến trúc mang tên AEGIS. Expert được stream từ SSD trong với F_NOCACHE, nên không có page cache phình ra. Một hot tier (tầng nóng) kích thước cố định giữ các expert được chọn nhiều nhất trong RAM và mượn bộ đệm prefill đang rảnh khi decode. Phép đoán liên lớp bắt đầu đọc expert của lớp kế tiếp trong lúc GPU còn bận, và expert 3-bit đóng gói làm mỗi lượt đọc nhỏ đi 22%.
    • Nguyên tắc: phỏng đoán không bao giờ thay đổi phép tính. Phỏng đoán chỉ quyết định đọc trước cái gì; định tuyến luôn dùng logit của chính mô hình, nên đầu ra giống hệt nhau ở mọi giới hạn.
    • Thời gian đi đâu: khoảng 138 ms mỗi token ở 4 GiB với expert 4-bit, gồm 61 ms kernel GPU, 50 ms chờ đọc SSD, và 27 ms việc của CPU cùng khoảng trống khi GPU thức dậy. 87 ms không phải chờ đĩa gần như không đổi theo giới hạn. Mục 6 biến điều đó thành công thức và chỉ ra đòn bẩy nào có tác dụng.

    1 · Vì sao phải stream expert

    Mô hình MoE chạy như một mô hình nhỏ, nếu expert ở gần

    Một lớp MoE chứa nhiều “expert” feed-forward, nhưng router chỉ gửi mỗi token tới vài expert trong số đó. Tổng số tham số lớn; số tham số mỗi token chạm tới thì nhỏ. Trên một máy dùng cho một người, tốc độ decode bị giới hạn bởi số byte phải di chuyển cho mỗi token chứ không phải bởi phép tính, nên mô hình MoE chạy như một mô hình nhỏ hơn nhiều, miễn là các expert nó cần đã nằm sẵn trong bộ nhớ nhanh.

    Các engine suy luận thường hỏi một mô hình cần bao nhiêu bộ nhớ rồi dừng ở đó. streamoe đảo ngược câu hỏi: đây là lượng bộ nhớ bạn được dùng; hãy làm cho mô hình vừa, rồi làm cho nó nhanh. Mọi thứ trong thiết kế đều xuất phát từ đó. Giới hạn là một cam kết, không phải ước lượng. Ổ đĩa là một tầng lưu trữ, không phải phương án dự phòng. Và mọi khẳng định đều được kiểm tra: so với MLX và transformers, CPU so với Metal, chạy theo lô so với từng token, với telemetry đếm hit, lượt đọc và thời gian chờ theo từng token, từng lớp và từng tầng. Những ý tưởng không mang lại kết quả cũng được ghi lại (vấn đề đã biết), để không ai phải thử lại một cách mù quáng.

    Flash-MoE và mlx-moe-streaming (mục 7) cho thấy stream expert hoạt động tốt cho suy luận một người dùng. Điều streamoe bổ sung là thiết kế hệ thống xung quanh: cam kết bộ nhớ, việc chia tầng, và phỏng đoán không bao giờ làm thay đổi đầu ra.

    2 · Phần cứng

    Một laptop M3 Pro đồng thời đang làm việc khác

    Thành phần · Thông số · Nguồn
    Thành phầnThông sốNguồn
    CPU11 nhân: 5 hiệu năng cao + 6 tiết kiệm điệnApple
    GPU14 nhânApple
    Bộ nhớ hợp nhất18 GB, 150 GB/s; 7–8 GiB có thể thu hồi trong lúc chạy, phần còn lại do các dịch vụ khác dùngApple
    SSD trongĐịnh danh ~6,7 GB/s; ~4,4 GB/s ở kích thước đọc của decode, 4,1–4,6 GB/s khi staging prefillĐo thực tế

    Hai điều định hình mọi thứ còn lại. Thứ nhất, Apple Silicon không có VRAM riêng: CPU và GPU dùng chung một vùng bộ nhớ, nên giới hạn phải bao gồm trọng số, KV cache, bộ nhớ tạm, checkpoint, phần chi phí của driver, bộ mã hóa hình ảnh, bộ đệm mạng, và cả page cache mà engine lẽ ra sẽ gây ra. Thứ hai, SSD không đạt tốc độ định danh với tải này. Một lượt đọc tám expert liền kề chỉ nhanh hơn 5–8% so với tám lượt đọc riêng, vì một expert 1,77 MB vốn đã là một khối lớn, còn chia lượt đọc thành nhiều mảnh thì chậm hơn. Decode bị giới hạn bởi ổ đĩa ở mức đỉnh cho các kích thước này, nên tốc độ đến từ việc đọc ít byte hơn.

    3 · Mô hình mục tiêu

    Qwen3.6-35B-A3B

    Qwen3.6-35B-A3B có khoảng 35B tham số tổng và khoảng 3B tham số hoạt động mỗi token. 40 lớp của nó gồm 30 lớp Gated DeltaNet (attention tuyến tính) và 10 lớp full attention. Mỗi lớp có 256 expert, mỗi expert khoảng 3,15M tham số; mỗi token dùng 8 expert được định tuyến cộng 1 expert dùng chung (tổng quan kiến trúc). Full attention dùng 2 KV head với head dimension 256. Mô hình dùng giấy phép Apache 2.0, nhận hình ảnh, và có ngữ cảnh gốc 262.144 token (ApX).

    Trọng số lấy từ mlx-community/Qwen3.6-35B-A3B-4bit (tải về 19,0 GiB) và được chuyển đổi một lần thành 1,29 GiB trọng số không phải expert, 16,9 GiB expert 4-bit và 0,84 GiB trọng số hình ảnh; expert 3-bit đóng gói cần thêm 13,1 GiB. Quá trình chuyển đổi dùng tối đa dưới 0,3 GiB RAM và mất khoảng 20 giây. Các kích thước quan trọng cho việc stream:

    Thuộc tính · Giá trị · Vì sao quan trọng
    Thuộc tínhGiá trịVì sao quan trọng
    Expert đã đóng gói1,69 MiB ở 4-bit; 1,31 MiB ở 3-bit đóng góiMỗi expert là một lượt đọc liền mạch; đủ nhỏ để cache hàng trăm cái.
    Byte expert định tuyến mỗi token8 × 40 × 1,69 MiB ≈ 541 MiBLưu lượng duy nhất có thể miss và chạm tới SSD.
    Toàn bộ expert định tuyến16,9 GiB (13,1 GiB ở 3-bit đóng gói)Nằm trên SSD; giới hạn 4 GiB giữ khoảng 6% trong RAM.
    Trọng số không phải expert1,29 GiBLuôn thường trú.
    KV cache40 KiB mỗi token ở F32Chỉ 10 lớp giữ K/V; trạng thái DeltaNet có kích thước cố định.

    Lưu lượng SSD đo được ở giới hạn 4 GiB là 258–413 MiB mỗi token với expert 4-bit, tùy prompt, và 87–477 MiB trên mọi giới hạn và định dạng expert đã thử. Mục 6 suy ra các con số trong bảng này.

    4 · Kiến trúc

    AEGIS: một giới hạn, lên kế hoạch khi mở; một tầng trên đĩa, một tầng trong RAM

    Thiết kế mang tên AEGIS: Adaptive · Expert · Guarded · In-memory · Streaming. Aegis là tấm khiên, và giới hạn bộ nhớ là vành khiên: không gì bên trong vượt qua nó, không gì bên ngoài bị ảnh hưởng, và mọi thứ bên trong cùng làm việc để chạy một mô hình lớn hơn tấm khiên nhiều lần.

    Chữ cáiNguyên tắcThành phần thực hiện
    A · AdaptiveĐiều chỉnh công việc theo giới hạn và theo những gì mô hình đang làmĐoán expert liên lớp, chờ prefetch theo từng slot, nhận vào hot tier theo LFU, cho mượn bộ đệm rảnh, expert 4-bit hoặc 3-bit đóng gói
    E · ExpertLàm cho mỗi expert được định tuyến rẻ khi đọc và khi chạyBố cục 3-bit đóng gói (ít hơn 22% byte, nhận diện khi mở), kernel gather hợp nhất, kernel matvec cho một token
    G · GuardedBộ nhớ là cam kết, không phải ước lượngKế hoạch bộ nhớ khi mở (OverBudget trước khi chạm vào bất cứ thứ gì), F_NOCACHE để các tiến trình bên cạnh giữ page cache, hình ảnh và KV nằm trong kế hoạch
    I · In-memoryByte đã nằm trong RAM phải làm được nhiều việc nhất có thểHot tier (gắn không sao chép), vòng slot, KV gọn (F16, Turbo4/3), staging prefill cho decode mượn
    S · StreamingỔ đĩa là một tầng, và thời gian của nó được đoLượt đọc F_NOCACHE song song chồng lên công việc GPU; trace thời gian I/O và GPU
    Engine streamoeKế hoạch bộ nhớmọi khoản định cỡ khi mởPrefetcherđoán liên lớp, 6 mỗi lớpQuản lý hot tierLFU, khởi tạo từ hồ sơNhóm I/Opread F_NOCACHE, chờ từng slotTính toánGPU: kernel MetalCPU: attention, hình ảnhRAM: giới hạn cứng 4 GiB, lên kế hoạch khi mở (M3 Pro, 18 GB, 150 GB/s)Trọng sốngoài expert, 1,29 GiBHot tier1,05 GiB + 0,42 GiB mượnKV cacheF32, 0,16 GiBRuntimebộ nhớ tạm, slotmiss + prefetchTầng đĩa nhanhbản sao tùy chọn của lớp nóngSSD trong, expert đã đóng gói~4,4 GB/s ở lượt đọc 1,7 MiB
    Hình 1. Engine streamoe, kế hoạch 4 GiB và các tầng đĩa (chế độ A, một máy Mac).

    4.1 Các thành phần của engine

    Thành phầnNhiệm vụGhi chú
    Kế hoạch bộ nhớĐịnh cỡ mọi khoản khi mở (n_ctx, định dạng KV, slot request, checkpoint, bộ đệm host); hot tier nhận phần còn lại.open_with báo lỗi OverBudget trước khi cấp phát bất cứ thứ gì.
    Nhóm I/Opread song song với F_NOCACHE thẳng vào bộ đệm slot; giới hạn độ sâu hàng đợi và số byte mỗi giây.Dưới giới hạn không có page cache phình ra, nên các tiến trình khác giữ được file cache của mình.
    Hot tierNhóm slot expert cố định; nhận theo LFU (một lượt miss chỉ thay một nạn nhân được lấy mẫu nếu nó nóng hơn); có thể khởi tạo từ hồ sơ định tuyến của các lần chạy trước.Lượt hit gắn slot RAM tại chỗ: không sao chép, không I/O.
    PrefetcherĐoán expert của lớp kế tiếp từ hidden state hiện tại và bắt đầu đọc trong lúc GPU làm việc.Gating liên lớp như trong Mixtral-offloading.
    Tính toánGated DeltaNet, full attention, router và phép matvec expert trên Metal; dưới giới hạn, attention khi decode của các lớp full attention chạy trên các luồng CPU.Kernel kế thừa từ Flash-MoE.
    Bộ mã hóa hình ảnhViT chạy trên CPU cho hình ảnh của Qwen3.5 / 3.6, nằm trong cùng giới hạn.Stream 0,85 GiB trọng số BF16 cho mỗi ảnh.
    TelemetryHit, lượt đọc và thời gian chờ theo từng token, từng lớp và từng tầng; timestamp GPU; trace dòng thời gian I/O.Nguồn của mọi bảng trong bài.

    4.2 G · Bộ nhớ: lên kế hoạch, không phải hy vọng

    Khi mở, streamoe định cỡ mọi khoản theo ngữ cảnh được yêu cầu, và hot tier nhận chính xác phần còn lại. Lượt đọc expert dùng F_NOCACHE, nên engine không bao giờ làm page cache phình ra và không tiến trình nào khác trên máy mất file cache. Nếu kế hoạch không vừa, việc mở thất bại trước khi chạm vào bất cứ thứ gì. Kế hoạch mà trang chat in ra cho Qwen3.6-35B-A3B với expert 4-bit dưới giới hạn 4 GiB (n_ctx 4096, KV F32):

    Khoản · GiB
    KhoảnGiB
    Hot tier (cache expert)1,05
    Trọng số không phải expert1,29
    Runtime / chi phí Metal0,50
    Staging prefill (mỗi expert của một lớp một slot)0,42
    Bộ nhớ tạm prefill0,23
    KV cache (F32)0,16
    Bộ đệm của bộ mã hóa hình ảnh0,13
    Trạng thái attention tuyến tính0,12
    Vòng slot expert0,09
    Tổng≈ 4,00

    Mọi khoản trừ hot tier cộng lại là 2,95 GiB. Với expert 3-bit đóng gói, staging prefill giảm còn 0,33 GiB và hot tier tăng lên 1,14 GiB. Trong các lần đo tốc độ, đỉnh bộ nhớ luôn thấp hơn giới hạn 0,26–0,31 GiB: 2,7, 3,7, 4,7 và 5,7 GiB ở giới hạn 3, 4, 5 và 6 GiB. Dưới khoảng 3 GiB các phần cố định không còn vừa và engine từ chối mở. Còn một ngoại lệ chưa xử lý: các lần chạy rất dài có thể vượt phần 0,5 GiB dành cho runtime. KV cache Turbo4 thu nhỏ phần của nó xuống 0,02 GiB, và hot tier lớn thêm đúng phần chênh lệch.

    4.3 I · Tái sử dụng: staging của prefill phục vụ decode

    Bộ đệm staging của prefill chứa mỗi expert của một lớp một slot và chỉ được ghi khi đọc prompt. Khi decode nó sẽ nằm không, nên hot tier mượn nó: thêm 256 slot expert bên cạnh 637 slot của chính nó, mà không vượt kế hoạch một byte nào. Prompt kế tiếp lấy lại bộ đệm trước chunk đầu tiên, và các slot đã mượn bị quên đi. Trên ba prompt thử ở giới hạn 4 GiB với expert 4-bit:

    Prompt · Hit hot tier, không mượn → có mượn · Decode, không mượn → có mượn
    PromptHit hot tier, không mượn → có mượnDecode, không mượn → có mượn
    Photosynthesis51% → 59%7,4 → 8,0 tok/s
    JavaScript22% → 34%6,0 → 6,4 tok/s
    Hiking42% → 54%7,1 → 7,6 tok/s
    Cả ba—6,9 → 7,4 tok/s (+7%)

    Việc dùng expert bị lệch, đó là lý do một tầng nhỏ vẫn đáng có. Phân tích định tuyến trong bài báo Mixtral cho thấy các token liên tiếp thường dùng chung expert (tóm tắt), và ở đây một hot tier chứa chưa tới một phần mười số expert phục vụ từ một phần ba tới hơn một nửa số lượt chọn. Ở giới hạn 3 GiB, bản thân hot tier gần như trống (0,05 GiB), vậy mà tỉ lệ hit vẫn đạt 22–38%: các slot mượn gánh phần đó.

    4.4 E · Expert 3-bit đóng gói

    Decode bị giới hạn bởi số byte đọc từ SSD, nên expert rẻ nhất là expert nhỏ nhất mà vẫn cho cùng câu trả lời. 3-bit đóng gói lưu 8 mã trong 3 byte thay vì 4. Kernel giữ nguyên thứ tự phần tử của 4-bit và , nên 3-bit đóng gói và 3-bit lưu theo nibble cho đầu ra giống hệt từng bit, và engine nhận diện bố cục khi mở dựa vào kích thước file của lớp. Expert 4-bit so với 3-bit đóng gói ở giới hạn 4 GiB:

    Chỉ số · 4-bit · 3-bit đóng gói
    Chỉ số4-bit3-bit đóng gói
    Byte mỗi expert1,69 MiB1,31 MiB (−22%)
    Toàn bộ expert định tuyến trên đĩa16,9 GiB13,1 GiB
    Hot tier1,05 GiB, 637 expert1,14 GiB, 893 expert
    Decode, ba prompt7,4 tok/s8,1 tok/s
    Chất lượng so với 4-bit—KL 0,048; cùng token top-1 93% số lần; perplexity 1,15 → 1,21; 10 trên 10 tác vụ chấm điểm

    3-bit đóng gói nhanh hơn 9–15% ở mọi giới hạn từ 3 đến 6 GiB, và ở 4 GiB nó nhanh gần bằng 4-bit ở 5 GiB. 2-bit làm hỏng lời gọi công cụ, số học, JSON và code (KL 0,29, trùng top-1 82%), nên không được đưa vào.

    4.5 A + S · Một lớp của decode

    Dưới giới hạn, mọi lượt đọc expert đều dùng F_NOCACHE. Mỗi lớp giấu càng nhiều I/O đó càng tốt phía sau công việc của GPU:

    • Khi lớp L bắt đầu, CPU đoán các expert của nó, hoặc từ hidden state của lớp L−1 (liên lớp) hoặc từ lựa chọn của token trước, bỏ những expert đã nóng, và gửi phần còn lại tới nhóm I/O.
    • Trong lúc các lượt đọc chạy, GPU tính attention và logit của router. Dưới giới hạn, các lớp full attention chạy trên các luồng CPU thay vì GPU.
    • CPU lấy top-k trên logit của chính mô hình. Lượt hit gắn slot RAM của nó; lượt miss được đọc song song vào các slot đồng bộ.
    • CPU chỉ chờ những slot prefetch mà lớp thực sự chọn. Một phỏng đoán sai đang đọc dở thì không bị chờ.
    • Phép matvec expert và bước kết hợp được nối thẳng vào norm của lớp L+1, và hot tier đếm lượt chọn rồi nhận các lượt miss nóng hơn một nạn nhân được lấy mẫu.

    Trên Qwen3.6-35B-A3B, phép đoán liên lớp với 6 expert prefetch dùng được 78–121 expert prefetch mỗi token, thay vì 13–35 với phép đoán theo token trước, và decode nhanh hơn 14%, với đầu ra giống hệt (expert 4-bit, giới hạn 4 GiB, mỗi hàng chỉ đổi một thiết lập; tok/s theo prompt là Photosynthesis / JavaScript / Hiking):

    Prefetch · tok/s theo prompt · tok/s, cả ba · Chờ đĩa, ms / token · Expert prefetch được dùng / token
    Prefetchtok/s theo prompttok/s, cả baChờ đĩa, ms / tokenExpert prefetch được dùng / token
    Liên lớp, 6 (chuẩn)8,0 / 6,4 / 7,67,439 / 67 / 4578 / 121 / 86
    Token trước, 4 (mặc định của thư viện)6,9 / 5,7 / 6,76,563 / 85 / 6813 / 35 / 20
    Liên lớp, 47,7 / 5,8 / 7,27,043 / 83 / 4954 / 81 / 57
    Liên lớp, 87,5 / 5,9 / 7,47,046 / 78 / 4698 / 151 / 108

    Sáu là độ rộng tốt nhất: bốn để lại lượt đọc cho sau, tám đọc nhiều hơn phần tiết kiệm được. Ổ đĩa rảnh khoảng 54 ms mỗi token, nhưng phần lớn thời gian đó đến thành từng mảnh khoảng 1 ms mỗi lớp trong lúc GPU chạy attention, quá ngắn để nhận thêm lượt đọc. Gửi các expert nóng cho GPU trước khi lượt đọc đĩa xong chỉ được 1–2%, nằm trong mức dao động giữa các lần chạy. CPU dùng khoảng 0,7 trên 11 nhân khi decode, và thêm luồng không giúp gì: 16 luồng I/O và 8 luồng attention cho kết quả như mặc định.

    4.6 Attention khi decode trên nhiều nhân

    Khi không dùng đường attention nhanh trên GPU (chạy có giới hạn, hoặc KV ở định dạng lượng tử hóa), attention của mỗi bước decode chạy trên CPU. Các query head độc lập với nhau, nên một nhóm luồng nhỏ (mặc định 4 luồng) nhận chúng một cách linh động. Mọi luồng đọc cùng một KV cache, chỉ đọc và dùng chung với GPU, và chỉ ghi phần đầu ra của các head mình xử lý, nên không cần khóa và kết quả giống hệt từng bit so với một luồng. Định dạng Turbo được chấm điểm trực tiếp trên mã 4 hoặc 3 bit đã lưu mà không giải nén cache. Trên OLMoE-1B-7B với ngữ cảnh 2,7k token, các luồng làm decode nhanh hơn 55–58%; ngữ cảnh ngắn được lợi ít, vì khi đó attention chỉ là một phần nhỏ của mỗi bước.

    4.7 Hình ảnh trong giới hạn

    Qwen3.5 và 3.6 nhận hình ảnh qua một bộ mã hóa hình ảnh chạy trên CPU, nằm trong cùng giới hạn. Ảnh được đổi kích thước về bội số của 32 (tối đa 640 × 640), cắt thành patch 16 pixel, đi qua các khối ViT với vị trí rotary 2 chiều, và gộp 2 × 2 thành token mà mô hình ngôn ngữ đọc ở vị trí M-RoPE của chúng. 0,85 GiB trọng số BF16 của nó được stream từ đĩa cho mỗi ảnh; 137 MiB bộ đệm của nó nằm trong kế hoạch. Bộ mã hóa khớp chính xác với transformers và khớp mlx_vlm ở mức 0,997. Đo ở giới hạn 4 GiB, vài thay đổi engine trước các lần đo tốc độ:

    Ảnh · Token ảnh · Mã hóa · Prefill, cả lượt · Bộ nhớ tiến trình
    ẢnhToken ảnhMã hóaPrefill, cả lượtBộ nhớ tiến trình
    448 × 3201401,1 s5,3 s3,69 GiB
    512 × 5122561,9 s5,7 s3,69 GiB
    640 × 6404003,2 s5,3 s3,71 GiB

    Decode sau một ảnh chạy với tốc độ bình thường: trong đoạn chat đã ghi, một lượt 309 token với 280 token ảnh được đọc trong khoảng 6 giây, rồi trả lời ở 5,9 tok/s với đỉnh 3,73 GiB. Cảnh vật, màu sắc, bố cục và đếm hoạt động tốt; chữ in nhỏ đọc kém, và MLX mắc cùng lỗi khi nhận cùng embedding, nên đây là giới hạn của mô hình chứ không phải của engine.

    4.8 Engine đến từ đâu

    streamoe phát triển từ moeflux của @mdegans, một bản viết lại bằng Rust của Flash-MoE. Việc làm mới bắt đầu trên bản fork moeflux của tôi, github.com/transybao1393/moeflux, nơi giới hạn bộ nhớ, stream F_NOCACHE và hot tier, chạy theo dải lớp, các định dạng KV, luồng attention khi decode và trang chat được xây dựng và công bố theo MIT cho tới commit 7d73350; sau đó việc phát triển chuyển sang streamoe. Nó giữ engine và kernel Metal kế thừa, cùng các bài kiểm tra so sánh với CPU và MLX, rồi xây lại mọi thứ xung quanh. Kernel Metal streaming-experts do Claude Opus 4.6 viết cho Flash-MoE.

    Repository này chạy trên một nút (chế độ A): một máy chạy mọi lớp trong giới hạn của nó. Việc chia một mô hình qua nhiều máy Mac (chế độ B) nằm ở một repository riêng xây trên engine này, dùng API theo dải của nó: chỉ mở và chạy các lớp a..b, với hidden state f32 vào và ra cùng snapshot theo từng dải.

    streamoe là mã nguồn công khai (source-available). Mọi thứ cho tới commit 7d73350 theo MIT; mọi thay đổi sau đó, bắt đầu từ đầu vào hình ảnh, theo PolyForm Noncommercial 1.0.0: miễn phí cho mục đích cá nhân, học thuật và phi thương mại khác, còn dùng cho mục đích thương mại cần giấy phép từ tác giả.

    5 · KV cache

    Nhỏ, vì chỉ 10 lớp giữ KV cache

    Vì chỉ 10 lớp full attention giữ KV cache, nó nhỏ ngay cả ở F32. Các lớp Gated DeltaNet mang một trạng thái kích thước cố định, không lớn lên theo ngữ cảnh. Engine lưu K/V ở F32 (mặc định), F16, hoặc TurboQuant 4 hay 3 bit, và snapshot lưu theo định dạng đang dùng. Kích thước mỗi token suy ra từ kiến trúc; kích thước Turbo4 lấy từ kế hoạch:

    Định dạng KV · Mỗi token · n_ctx 4096 · Toàn bộ ngữ cảnh 262K
    Định dạng KVMỗi tokenn_ctx 4096Toàn bộ ngữ cảnh 262K
    F3240 KiB0,16 GiB10 GiB
    F1620 KiB0,08 GiB5 GiB
    Turbo4~5 KiB0,02 GiB~1,3 GiB

    Trên Qwen3.6-35B-A3B với n_ctx 4096, cache đủ nhỏ để F32 tốn ít và Turbo4 không đem lại lợi ích đo được; câu trả lời khớp MLX với độ trùng top-20 là 20/20 ở F32 và 19/20 ở Turbo4. Với các mô hình có cache lớn thì khác: trên OLMoE-1B-7B ở giới hạn 3 GiB, F16 và Turbo4 làm decode nhanh hơn 29% và 44%, vì phần bộ nhớ được giải phóng chuyển sang hot tier. TurboQuant báo cáo chất lượng không đổi ở 3,5 bit mỗi kênh trên Llama-3.1-8B-Instruct (tóm tắt của TechInformed), và một đánh giá độc lập tìm thấy một số lớp key cần nhiều bit hơn (turboquant-eval). Multi-head latent attention (Cogito) giữ ở F32.

    6 · Phần toán

    Nhanh đến đâu, và vì sao?

    Với batch size 1, một token chờ ba thứ: kernel GPU; việc của CPU (định tuyến, mã hóa lệnh, sổ sách hot tier) cùng khoảng trống khi GPU thức dậy; và phần lượt đọc SSD mà công việc GPU không giấu được. Hai thứ đầu gần như không phụ thuộc vào giới hạn. Nếu một token đọc d byte từ SSD ở khoảng 4,4 GB/s, và một phần φ thời gian đọc đó chồng lên công việc GPU:

    ƒ(x)Thời gian mỗi token

    Mọi đầu vào đều đo trong cấu hình chuẩn của repo: n_ctx 4096, KV F32, phép đoán liên lớp với 6 expert prefetch, decode greedy, và ba prompt chat (Photosynthesis, JavaScript, Hiking), mỗi giá trị là trung bình của hai lần chạy. Tỉ lệ hit và lượt đọc đĩa tính theo từng prompt:

    Giới hạn · Expert · Hot tier · tok/s, cả ba · Hit hot tier · Đọc đĩa MiB / token · Đỉnh
    Giới hạnExpertHot tiertok/s, cả baHit hot tierĐọc đĩa MiB / tokenĐỉnh
    3 GiB4-bit0,05 GiB6,538 / 22 / 35%372 / 477 / 3922,73 GiB
    3 GiB3-bit đóng gói0,14 GiB7,542 / 23 / 35%278 / 368 / 3102,69 GiB
    4 GiB4-bit1,05 GiB7,459 / 34 / 54%258 / 413 / 2893,74 GiB
    4 GiB3-bit đóng gói1,14 GiB8,164 / 38 / 54%180 / 305 / 2253,73 GiB
    5 GiB4-bit2,05 GiB7,970 / 45 / 64%190 / 350 / 2304,74 GiB
    5 GiB3-bit đóng gói2,14 GiB8,676 / 51 / 66%124 / 248 / 1744,73 GiB
    6 GiB4-bit3,05 GiB8,378 / 54 / 72%141 / 296 / 1875,74 GiB
    6 GiB3-bit đóng gói3,14 GiB9,284 / 62 / 74%87 / 198 / 1355,72 GiB

    Mỗi GiB thêm vào tăng 5–14%, nhiều nhất là từ 3 lên 4 GiB với expert 4-bit. Prompt JavaScript trải trên nhiều expert nhất, nên đọc đĩa nhiều nhất ở mọi giới hạn. Đầu ra giống hệt nhau ở mọi giới hạn; với expert 4-bit, argmax trùng bản tham chiếu MLX và độ trùng top-20 là 20/20.

    6.1 Các con số đến từ đâu

    Mọi con số trong bài đều suy ra từ vài thông số kiến trúc đã công bố và vài số đo. Ở mỗi bước dưới đây, công thức được viết bằng ký hiệu trước, và các dòng bên dưới thay số của máy này vào, để có thể kiểm tra và dùng lại cho mô hình và phần cứng khác.

    Ký hiệu · Ý nghĩa · Giá trị ở đây
    Ký hiệuÝ nghĩaGiá trị ở đây
    Số trọng số mỗi expert3,15M
    Số bit mỗi trọng số4 (3 khi đóng gói)
    Bit scale và bias mỗi trọng số (16 + 16 bit cho mỗi nhóm 64)0,5
    Số expert định tuyến mỗi lớp256
    Số expert định tuyến mỗi token, mỗi lớp8
    Số lớp40
    Số lớp full attention (chỉ chúng có KV cache)10
    Số KV head2
    Head dimension256
    Byte mỗi giá trị KV4 (F32)
    Giới hạn bộ nhớ4 GiB
    Các khoản cố định trong kế hoạch (mọi thứ trừ hot tier)2,95 GiB
    Băng thông đọc SSD ở kích thước đọc của decode (đo)4,4 GB/s
    Thời gian GPU mỗi token (đo)61 ms
    Việc của CPU và khoảng trống khi GPU thức dậy mỗi token (đo)27 ms
    Thời gian mỗi token không phải chờ đĩa (đo, 84–92 ms tùy ô)≈ 87 ms
    Tỉ lệ hit hot tier0 đến 1
    Số token trong ngữ cảnh—
    Byte mỗi expert đã đóng gói (bước 1)1,69 MiB
    Thời gian đọc một expert (bước 1)≈ 0,40 ms
    Byte expert định tuyến mỗi token (bước 2)541 MiB
    Byte của toàn bộ expert định tuyến (bước 2)16,9 GiB
    Byte đọc từ SSD mỗi token (bước 2)258–413 MiB
    Byte KV cache mỗi token (bước 3)40 KiB
    Byte KV cache cho ngữ cảnh n token (bước 3)—
    Kích thước hot tier (bước 4)1,05 GiB
    Số slot hot tier (bước 4)≈ 637
    Tỉ lệ expert định tuyến mà hot tier giữ (bước 4)6,2%
    Tỉ lệ thời gian đọc SSD được giấu sau công việc GPU (bước 5)≈ 0,35
    Thời gian chờ SSD mỗi token (bước 5)39–67 ms
    Số token mỗi giây (bước 5)—
    ƒ(x)Bước 1 · Byte mỗi expert

    Định dạng 4-bit lưu một scale 16 bit và một bias 16 bit cho mỗi 64 trọng số, tức thêm nửa bit mỗi trọng số. Vì vậy một expert 4-bit nặng 1,69 MiB thay vì 1,5 MiB nếu chỉ tính mã, và 3-bit đóng gói nặng 1,31 MiB, nhỏ hơn 22%, đúng bằng kích thước engine đọc. Bộ đệm staging của prefill là (0,33 GiB với 3-bit đóng gói), đúng khoản trong kế hoạch.

    ƒ(x)Bước 2 · Lưu lượng expert mỗi token

    Một token dùng số expert ở mỗi lớp, vậy mà các lượt đọc đó cộng lại hơn nửa gigabyte. là lượng SSD phải cấp nếu mọi phỏng đoán đều đúng. Với prompt Photosynthesis ở 4 GiB (59% hit), nó dự đoán 222 MiB mỗi token; repo đo được 258. Chênh lệch 36 MiB tương đương khoảng 21 expert prefetch mỗi token đã được đoán nhưng không được chọn. Với 3-bit đóng gói, là 13,1 GiB.

    ƒ(x)Bước 3 · KV cache mỗi token

    Số 2 tính cả K và V. là khoản KV trong kế hoạch. Nếu cả 40 lớp đều dùng full attention, sẽ làm thành 160 KiB, gấp bốn lần; chính 30 lớp DeltaNet giúp F32 vẫn vừa túi.

    ƒ(x)Bước 4 · Hot tier giữ bao nhiêu phần mô hình

    Hot tier giữ khoảng 637 trên 10.240 expert định tuyến, tức 6%, và 893 (8,7%) khi bộ đệm staging được cho mượn, vậy mà phục vụ 34–59% lượt chọn ở giới hạn này. Số hạng cuối là sự đánh đổi: mỗi gibibyte dành cho KV cache dài hơn hay bộ đệm lớn hơn tốn khoảng 600 slot.

    ƒ(x)Bước 5 · Thời gian mỗi token
    Prompt (4 GiB, 4-bit) · Đo được
    Prompt (4 GiB, 4-bit)Đọc đĩa Tốc độ Đo được
    Photosynthesis258 MiB40 ms127 ms7,9 tok/s8,0 tok/s
    JavaScript413 MiB64 ms151 ms6,6 tok/s6,4 tok/s
    Hiking289 MiB45 ms132 ms7,6 tok/s7,6 tok/s
    Mọi expert trong RAM00 ms87 ms11,5 tok/s— (ước tính)

    lấy từ ba prompt thử ở 4 GiB: 0,37, 0,32 và 0,35 cho Photosynthesis, JavaScript và Hiking (dòng trên là Hiking, đọc 289 MiB mỗi token). Khoảng một phần ba thời gian đọc được giấu sau công việc GPU. Với , mô hình nằm trong khoảng 0,2 tok/s so với mọi số đo bên dưới. Khi mọi expert nằm trong RAM, và decode sẽ đạt khoảng 11,5 tok/s; đó là ước tính của chính repo, vì máy này không thể dành ra 16–20 GiB để đo.

    Công thức nói gì

    • Chờ đĩa giờ là phần nhỏ hơn. 87 ms không phải chờ đĩa giữ nguyên từ 3 đến 6 GiB, trong khi là 39–67 ms ở 4 GiB. Giới hạn lớn hơn tăng hit và giảm , được 5–14% mỗi GiB, với lợi ích giảm dần.
    • Số byte mỗi lượt miss là một đòn bẩy, và nó đã được đưa vào. tỉ lệ với . Expert 3-bit đóng gói () làm còn 7/9; đo được, nhanh hơn 9–15% ở mọi giới hạn, với KL 0,048 và 93% trùng top-1 so với 4-bit.
    • GPU là phần lớn nhất. Timestamp GPU cho thấy kernel khi decode chạy chậm hơn 3–4 lần so với cùng kernel chạy riêng: một lần dispatch 8 expert mất 0,22 ms trên GPU đã ấm và khoảng 0,75 ms trong lúc decode. CPU phải thấy định tuyến của mỗi lớp rồi mới lấy được expert của lớp đó, và các đợt công việc ngắn này để GPU ở trạng thái chậm. Gửi các expert nóng trước khi lượt đọc đĩa xong là bước đầu tiên.
    • Băng thông không phải đòn bẩy trên máy này. đã là mức đỉnh của ổ cho lượt đọc 1,77 MB. Lượt đọc lớn hơn hoặc tuần tự nhanh hơn tối đa 5–8%, chia nhỏ lượt đọc thì chậm hơn, và prefetch rộng hơn (10–16 phỏng đoán) còn chậm hơn nữa: miss giảm, nhưng tổng số byte tăng nhanh hơn.
    • Những gì mô hình bỏ qua. được khớp trên ba prompt ngắn ở một giới hạn; lượt đọc của phỏng đoán sai nằm trong đo được; prefill và ngữ cảnh dài không được mô hình hóa. Hãy xem nó như chỉ dẫn về đòn bẩy nào làm thay đổi thời gian, đã kiểm tra với số đo.

    6.2 Điều gì sẽ thay đổi các con số tiếp theo

    • Thời gian tới chữ đầu tiên. Staging expert chiếm khoảng 74% prefill và chạy từng lớp một trong khi CPU rảnh khoảng 95%. Staging lớp L+1 trong lúc GPU tính lớp L có thể cắt một phần ba prefill.
    • Độ chính xác theo từng expert. Sau định tuyến, một chính sách bên ngoài engine sẽ quyết định cho từng expert chạy ở 4-bit, 3-bit đóng gói, hay bỏ qua, đổi chất lượng lấy I/O từng expert một; HOBBIT phục vụ miss ở độ chính xác thấp hơn theo cùng tinh thần (HOBBIT). Chỉ bỏ qua thôi thì có vẻ yếu trên mô hình này: trọng số gate của nó khá phẳng, 6 trên 8 lựa chọn đứng đầu mang 84% trọng số, nên bỏ lựa chọn sẽ tốn chất lượng thực sự.
    • Nhiều token mỗi lượt. Decode một token cho mỗi request trong 4 request cùng một lượt cho thông lượng tổng gấp 1,30–1,33 lần (11,6–11,9 tok/s, 3-bit đóng gói, giới hạn 6 GiB), với từng request chậm hơn. Speculative decoding với bản nháp prompt-lookup chưa có lợi: 0,79–1,02 lần so với decode thường.
    • Hình ảnh. Một ảnh tốn 1–3 giây mã hóa và một lượt prefill 5–6 giây, sau đó decode chạy với tốc độ bình thường.

    7 · Các công trình hiện có

    Khác gì so với các công trình hiện có

    Dự ánLàm gìKhác biệt ở đây
    Flash-MoEEngine C/Objective-C/Metal cho Qwen3.5-397B, stream expert từ SSD trong; dựa vào page cache của hệ điều hành; công bố nhật ký 58 thí nghiệm.Giới hạn bộ nhớ cứng không dùng page cache, hot tier tường minh, hình ảnh; mã host bằng Rust.
    moefluxBản viết lại bằng Rust phần host của Flash-MoE trên Metal; hỗ trợ Qwen3.6-35B-A3B.streamoe bắt đầu trên bản fork của tôi và giữ engine của nó; bổ sung cam kết bộ nhớ, chia tầng, prefetch, định dạng KV, expert 3-bit đóng gói và hình ảnh.
    mlx-moe-streamingLớp MLX stream expert; đo trên M1 8 GB, kể cả với SSD gắn ngoài.Không có Python/MLX khi chạy; lượt đọc đi thẳng vào slot riêng của engine.
    llama.cpp #25294PR đang mở bổ sung stream expert với cache tường minh và prefill theo đợt.Bộ nhớ có kế hoạch, prefetch liên lớp, đầu ra giống hệt ở mọi giới hạn.

    Không ý tưởng riêng lẻ nào là mới, và phần toán vẫn là đại số tuyến tính mà mọi engine suy luận đều chạy. Điều streamoe đóng góp là thiết kế hệ thống: một cam kết bộ nhớ bao gồm cả page cache, một hot tier cố định mượn các bộ đệm đang rảnh, và phỏng đoán chỉ quyết định đọc trước cái gì.

    8 · Đánh đổi

    Những gì đã đo rồi bỏ, và những gì chưa bao giờ được chọn

    • Kiểu đọc nhanh hơn. Một lượt đọc tám expert liền kề chỉ nhanh hơn 5–8% so với tám lượt đọc riêng (3,14 so với 3,39 ms), và chia mỗi lượt đọc thành 2–8 mảnh thì chậm hơn. Ổ đạt đỉnh khoảng 4,4 GB/s cho các kích thước này, không phải 6,7 GB/s định danh.
    • Lấp thời gian rảnh của ổ đĩa. Prefetch rộng hơn (10, 12 hoặc 16 phỏng đoán) chậm hơn, tới 258 ms mỗi token, và đoán trước hai lớp kém hơn một lớp (5,75 so với 6,37 trên 8 lựa chọn đúng).
    • Thêm luồng. 16 luồng I/O và 8 luồng attention cho kết quả như mặc định; CPU chủ yếu đang chờ GPU.
    • Khởi động ấm hot tier ở giới hạn nhỏ. Ở 4 GiB tầng này giữ 637 expert và mỗi token định tuyến 320, nên tầng được thay mới ngay trong vài token đầu của bất kỳ prompt nào; khởi động có hồ sơ và khởi động lạnh đo ra như nhau.
    • Expert 2-bit. KL 0,29 và 82% trùng top-1 so với 4-bit, và chúng làm hỏng một lời gọi công cụ, số học, một câu trả lời JSON và code. 3-bit giữ được chất lượng và được đưa vào dưới dạng 3-bit đóng gói.
    • vLLM hoặc SGLang làm runtime. Cả hai tự quản lý bộ nhớ và việc nạp trọng số, xung đột với giới hạn cứng và stream không sao chép, và continuous batching kích hoạt gần như mọi expert mỗi bước. Một runtime Metal gốc cũng đã được chứng minh nhanh hơn MLX tới 1,35 lần khi decode (BaseRT).
    • Neural Engine cho expert. Core ML quay về CPU hoặc GPU khi kích thước thực sự động (tài liệu ExecuTorch Core ML), và trọng số được biên dịch vào mô hình, không hợp với việc stream expert theo từng token.

    9 · Tiếp theo

    Lộ trình

    • Chữ đầu tiên đến nhanh hơn: staging expert của lớp L+1 trong lúc GPU tính lớp L.
    • Chính sách router và độ chính xác theo từng expert: 4-bit, 3-bit đóng gói hay bỏ qua, do một chính sách bên ngoài engine chọn cho từng expert.
    • Ít đợt GPU hơn nhưng dài hơn: đưa thêm phần việc của mỗi lớp lên trước điểm đồng bộ định tuyến của lớp đó, để thu nhỏ 87 ms mà giới hạn lớn hơn không xóa được.
    • Thêm họ mô hình: Qwen3-30B-A3B như một biến thể mới (ước tính, chưa đo: khoảng 0,95 GiB đọc mỗi token khi hot tier lạnh, gấp đôi Qwen3.6-35B-A3B), sau đó chọn biến thể lúc chạy thay vì mỗi mô hình một feature lúc biên dịch.
    • Kiểm chứng ở kích thước đầy đủ: oracle không giới hạn, các bài kiểm tra chế độ mmap, và các mô hình Qwen3.5 và Cogito lớn hơn cần một máy dư ra ít nhất 8 GiB.
    • Cấu hình ban đầu. Mac mini M4 đọc từ SSD gắn ngoài qua Thunderbolt 4, đo được khoảng 3,2 GB/s tuần tự trong một bài thử enclosure (CNX Software). Đưa con số đó và mức trung bình 320 MiB đọc mỗi token ở 4 GiB vào bước 5 cho , khoảng 6,4 tok/s (tính toán, và lạc quan, vì lượt đọc nhỏ sẽ không đạt tốc độ tuần tự).