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ần | Thông số | Nguồn |
|---|---|---|
| CPU | 11 nhân: 5 hiệu năng cao + 6 tiết kiệm điện | Apple |
| GPU | 14 nhân | Apple |
| Bộ nhớ hợp nhất | 18 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ùng | Apple |
| 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ính | Giá trị | Vì sao quan trọng |
|---|---|---|
| Expert đã đóng gói | 1,69 MiB ở 4-bit; 1,31 MiB ở 3-bit đóng gói | Mỗ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 token | 8 × 40 × 1,69 MiB ≈ 541 MiB | Lưu lượng duy nhất có thể miss và chạm tới SSD. |
| Toàn bộ expert định tuyến | 16,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 expert | 1,29 GiB | Luôn thường trú. |
| KV cache | 40 KiB mỗi token ở F32 | Chỉ 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ái | Nguyên tắc | Thà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 · Expert | Làm cho mỗi expert được định tuyến rẻ khi đọc và khi chạy | Bố 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 · Guarded | Bộ nhớ là cam kết, không phải ước lượng | Kế 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-memory | Byte đã 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 đo | Lượt đọc F_NOCACHE song song chồng lên công việc GPU; trace thời gian I/O và GPU |
4.1 Các thành phần của engine
| Thành phần | Nhiệ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/O | pread 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 tier | Nhó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án | Gated 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 ảnh | ViT 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. |
| Telemetry | Hit, 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ản | GiB |
|---|---|
| Hot tier (cache expert) | 1,05 |
| Trọng số không phải expert | 1,29 |
| Runtime / chi phí Metal | 0,50 |
| Staging prefill (mỗi expert của một lớp một slot) | 0,42 |
| Bộ nhớ tạm prefill | 0,23 |
| KV cache (F32) | 0,16 |
| Bộ đệm của bộ mã hóa hình ảnh | 0,13 |
| Trạng thái attention tuyến tính | 0,12 |
| Vòng slot expert | 0,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
| Prompt | Hit hot tier, không mượn → có mượn | Decode, không mượn → có mượn |
|---|---|---|
| Photosynthesis | 51% → 59% | 7,4 → 8,0 tok/s |
| JavaScript | 22% → 34% | 6,0 → 6,4 tok/s |
| Hiking | 42% → 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-bit | 3-bit đóng gói |
|---|---|---|
| Byte mỗi expert | 1,69 MiB | 1,31 MiB (−22%) |
| Toàn bộ expert định tuyến trên đĩa | 16,9 GiB | 13,1 GiB |
| Hot tier | 1,05 GiB, 637 expert | 1,14 GiB, 893 expert |
| Decode, ba prompt | 7,4 tok/s | 8,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
| Prefetch | tok/s theo prompt | tok/s, cả ba | Chờ đĩa, ms / token | Expert prefetch được dùng / token |
|---|---|---|---|---|
| Liên lớp, 6 (chuẩn) | 8,0 / 6,4 / 7,6 | 7,4 | 39 / 67 / 45 | 78 / 121 / 86 |
| Token trước, 4 (mặc định của thư viện) | 6,9 / 5,7 / 6,7 | 6,5 | 63 / 85 / 68 | 13 / 35 / 20 |
| Liên lớp, 4 | 7,7 / 5,8 / 7,2 | 7,0 | 43 / 83 / 49 | 54 / 81 / 57 |
| Liên lớp, 8 | 7,5 / 5,9 / 7,4 | 7,0 | 46 / 78 / 46 | 98 / 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
| Ảnh | Token ảnh | Mã hóa | Prefill, cả lượt | Bộ nhớ tiến trình |
|---|---|---|---|---|
| 448 × 320 | 140 | 1,1 s | 5,3 s | 3,69 GiB |
| 512 × 512 | 256 | 1,9 s | 5,7 s | 3,69 GiB |
| 640 × 640 | 400 | 3,2 s | 5,3 s | 3,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 KV | Mỗi token | n_ctx 4096 | Toàn bộ ngữ cảnh 262K |
|---|---|---|---|
| F32 | 40 KiB | 0,16 GiB | 10 GiB |
| F16 | 20 KiB | 0,08 GiB | 5 GiB |
| Turbo4 | ~5 KiB | 0,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ạn | Expert | Hot tier | tok/s, cả ba | Hit hot tier | Đọc đĩa MiB / token | Đỉnh |
|---|---|---|---|---|---|---|
| 3 GiB | 4-bit | 0,05 GiB | 6,5 | 38 / 22 / 35% | 372 / 477 / 392 | 2,73 GiB |
| 3 GiB | 3-bit đóng gói | 0,14 GiB | 7,5 | 42 / 23 / 35% | 278 / 368 / 310 | 2,69 GiB |
| 4 GiB | 4-bit | 1,05 GiB | 7,4 | 59 / 34 / 54% | 258 / 413 / 289 | 3,74 GiB |
| 4 GiB | 3-bit đóng gói | 1,14 GiB | 8,1 | 64 / 38 / 54% | 180 / 305 / 225 | 3,73 GiB |
| 5 GiB | 4-bit | 2,05 GiB | 7,9 | 70 / 45 / 64% | 190 / 350 / 230 | 4,74 GiB |
| 5 GiB | 3-bit đóng gói | 2,14 GiB | 8,6 | 76 / 51 / 66% | 124 / 248 / 174 | 4,73 GiB |
| 6 GiB | 4-bit | 3,05 GiB | 8,3 | 78 / 54 / 72% | 141 / 296 / 187 | 5,74 GiB |
| 6 GiB | 3-bit đóng gói | 3,14 GiB | 9,2 | 84 / 62 / 74% | 87 / 198 / 135 | 5,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ĩa | Giá trị ở đây |
|---|---|---|
| Số trọng số mỗi expert | 3,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ớp | 256 | |
| Số expert định tuyến mỗi token, mỗi lớp | 8 | |
| Số lớp | 40 | |
| Số lớp full attention (chỉ chúng có KV cache) | 10 | |
| Số KV head | 2 | |
| Head dimension | 256 | |
| Byte mỗi giá trị KV | 4 (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 tier | 0 đế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 | ||
|---|---|---|---|---|---|
| Photosynthesis | 258 MiB | 40 ms | 127 ms | 7,9 tok/s | 8,0 tok/s |
| JavaScript | 413 MiB | 64 ms | 151 ms | 6,6 tok/s | 6,4 tok/s |
| Hiking | 289 MiB | 45 ms | 132 ms | 7,6 tok/s | 7,6 tok/s |
| Mọi expert trong RAM | 0 | 0 ms | 87 ms | 11,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ự án | Làm gì | Khác biệt ở đây |
|---|---|---|
| Flash-MoE | Engine 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. |
| moeflux | Bả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-streaming | Lớ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 #25294 | PR đ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ự).