25 KiB
Note
本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
English · 原始项目 · 上游 README
原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
cider
Cider 基于 MLX 为 macOS 开发。它提供:
- MLX 所不具备的在线激活量化(W8A8/W4A8),配有自定义 INT8 TensorOps 内核,作为 MLX 自定义原语(custom primitives)构建,支持完整的惰性求值(lazy evaluation)。
- 受 FlashInfer, 启发的优化 SDPA 解码内核,采用连续块 GQA 调度与寄存器分块(register tiling),以实现更快的自回归解码。
它还包含服务端扩展以及对 mlx_vlm 的非侵入式兼容性补丁(已在 mlx_vlm 0.4.3 上验证),包括修复与 RoPE 位置处理及分块预填充(chunked prefill)相关的 Qwen3-VL 多图推理问题。
条件编译(M4 / M5)
Cider 采用条件编译:INT8 TensorOps C++ 扩展仅在 Apple M5+ 上构建。
| Chip | pip install -e . behavior |
import cider behavior |
|---|---|---|
| M5+ | 完整构建(CMake + Metal kernels) | 全部功能可用 |
| M4 及以下 | 跳过 C++ 构建,安装纯 Python 包 | is_available() → False,convert_model() 为警告空操作(warning no-op) |
通过环境变量覆盖:
CIDER_FORCE_BUILD=1 pip install -e . # Force build (e.g., CI)
CIDER_FORCE_BUILD=0 pip install -e . # Force skip
模式
| Mode | Weights | Activations | Compute Path | Status |
|---|---|---|---|---|
| W8A8 | INT8 symmetric | INT8 per-token | TensorOps matmul2d | ✅ 已实现 |
| W4A8 | INT4 packed (uint8) | INT8 per-token | Unpack→TensorOps | ✅ 已实现 |
| W4A16 | — | — | MLX built-in | 基线 |
| W8A16 | — | — | MLX built-in | 基线 |
W4A16 和 W8A16 已由 MLX 原生支持 — 本 SDK 提供 MLX 未实现的 W8A8 与 W4A8 模式。
优化 SDPA(解码注意力)
Cider 包含面向自回归解码(Q_seq=1)的优化缩放点积注意力(Scaled Dot-Product Attention)内核,受 FlashInfer. 启发。关键技术:
- 连续块 GQA 调度(Contiguous Chunk GQA Scheduling):不再让每个 Q 头独立派发(读取 KV
H_q次),而是由单个线程组处理共享同一 KV 组的所有 Q 头。这可减少最多gqa_factor× 的 DRAM 带宽。 - 寄存器分块(Register Tiling,TILE=4):每个线程在寄存器中保存 4 个 Q 头的部分结果 — 无需共享内存,无需屏障同步 — 最大化分组查询解码的 ALU 吞吐。
- 两遍 FlashDecoding(2-Pass FlashDecoding):对于长序列(≥1024 tokens),两遍归约将 KV 序列划分到各线程组,以提升占用率。
- AutoTune:针对(架构、GQA 比率、头数、序列长度)自动扫描块大小,并缓存至
~/.cider_sdpa_tune.json。
用法
import cider
# One-line patch — all downstream frameworks (mlx_lm, mlx_vlm) benefit automatically
cider.patch_sdpa()
# Optional: run AutoTune once to find optimal block sizes for your chip
cider.autotune_sdpa()
# Undo patch if needed
cider.unpatch_sdpa()
该补丁完全透明:预填充(Q_seq > 1)与带掩码注意力会自动回退到 MLX 原生实现。仅解码步骤(Q_seq=1,无掩码)会走 Cider 的优化内核。无需修改 mlx_lm 或 mlx_vlm 代码。
SDPA 微基准(Apple M5 Pro,D=128)
相对 MLX mx.fast.scaled_dot_product_attention 的加速比:
| Config | N=1K | N=2K | N=4K | N=8K | N=16K | N=32K |
|---|---|---|---|---|---|---|
| MHA (H_q=32, H_kv=32) | 1.03x | 1.01x | 1.02x | 1.00x | 1.00x | 0.99x |
| GQA4 (H_q=32, H_kv=8) | 1.04x | 1.06x | 1.06x | 1.12x | 1.16x | 1.21x |
| GQA8 (H_q=32, H_kv=4) | 1.04x | 1.07x | 1.13x | 1.21x | 1.34x | 1.57x |
加速范围:0.99×–1.57×。正确性:18/18 PASS(相对 MLX 最大差异 ≤ 0.000122,fp16)。14 WIN / 4 TIE / 0 LOSS — 零回退。
启用 SDPA 的端到端解码速度(Qwen3-VL-4B,Apple M5 Pro)
使用 benchmarks/bench_e2e_wxa16.py 在真实多图 VLM 轨迹上测试(1–3 张截图,1334–3455 prompt tokens):
| Config | Decode tok/s | vs Baseline |
|---|---|---|
| FP16 | 32.0 | — |
| FP16 + Cider SDPA | 32.6 | +1.9% |
| W8A16 | 54.0 | — |
| W8A16 + Cider SDPA | 58.0 | +7.4% |
| W8A8 per-group | 51.0 | — |
| W8A8 per-group + Cider SDPA | 52.3 | +2.5% |
| W8A8 per-channel | 49.1 | — |
| W8A8 per-channel + Cider SDPA | 52.6 | +7.1% |
Cider SDPA 可叠加在 W8A8 预填充收益之上。收益随 GQA 比率与 KV cache 长度而放大 — GQA 比率更高(例如 GQA8 的 Qwen3-32B)且上下文更长的模型获益更大。
MLX 的量化是仅权重量化(weight-only):QuantizedLinear 将权重反量化为 FP16 并使用 FP16 GEMM。虽然 MLX 的 Steel NAX 模板足够通用,可实例化为 INT8 类型(且能达到相同的原始 matmul 吞吐 — 参见我们的透明基准测试),但 MLX 并未提供实际 W8A8 推理所需的量化/反量化流水线。Cider 通过融合的 quantize-matmul-dequant 原语填补这一缺口。
本 SDK 为受支持的推理路径实现在线 INT8 激活量化,以及基于 INT8 TensorOps 的计算。
W8A8 量化粒度
| Granularity | Description | Speed | Precision |
|---|---|---|---|
| Per-channel | 每个输出通道一个 scale | 最快(预填充 1.8x) | 略低 |
| Per-group (gs=128) | 每 128 个元素一个 scale | 较快(预填充 1.5x) | 精度保留适中 |
| Per-group (gs=64) | 每 64 个元素一个 scale | 中等(预填充 1.3x) | 精度更高 |
性能(Apple M5 Pro)
单算子延迟
Shape [N=10240, K=2560]
| M | PC(ms) | PG(ms) | w8a16 | w4a16 | PC/w8 | PC/w4 | PG/w8 | PG/w4 |
|---|---|---|---|---|---|---|---|---|
| 1 | 0.27ms | 0.26ms | 0.26ms | 0.18ms | 0.96x | 0.67x | 0.99x | 0.69x |
| 128 | 0.34ms | 0.39ms | 0.49ms | 0.44ms | 1.43x | 1.28x | 1.26x | 1.13x |
| 1024 | 1.23ms | 1.52ms | 2.24ms | 2.04ms | 1.82x | 1.66x | 1.47x | 1.34x |
| 4096 | 4.41ms | 5.65ms | 8.12ms | 7.72ms | 1.84x | 1.75x | 1.44x | 1.37x |
| 8192 | 8.71ms | 11.40ms | 16.23ms | 15.09ms | 1.86x | 1.73x | 1.42x | 1.32x |
Shape [N=2560, K=10240]
| M | PC(ms) | PG(ms) | w8a16 | w4a16 | PC/w8 | PC/w4 | PG/w8 | PG/w4 |
|---|---|---|---|---|---|---|---|---|
| 1 | 0.25ms | 0.26ms | 0.26ms | 0.20ms | 1.03x | 0.78x | 0.98x | 0.75x |
| 128 | 0.39ms | 0.41ms | 0.55ms | 0.46ms | 1.43x | 1.19x | 1.35x | 1.12x |
| 1024 | 1.31ms | 1.65ms | 2.35ms | 2.14ms | 1.80x | 1.64x | 1.43x | 1.30x |
| 4096 | 5.37ms | 6.79ms | 8.54ms | 8.04ms | 1.59x | 1.50x | 1.26x | 1.18x |
| 8192 | 10.97ms | 12.94ms | 17.28ms | 16.23ms | 1.58x | 1.48x | 1.34x | 1.25x |
端到端 VLM
Qwen3-VL-2B
| Prompt Tokens | FP16 Prefill (tok/s) | W8A16 Prefill (tok/s) | W8A8 PC Prefill (tok/s) | FP16 Decode (tok/s) | W8A16 Decode (tok/s) | W8A8 PC Decode (tok/s) |
|---|---|---|---|---|---|---|
| 1334 | 3010 | 2065 | 3242 | 70 | 107 | 104 |
| 2393 | 2868 | 1847 | 2983 | 69 | 97 | 100 |
| 3455 | 2777 | 1741 | 2796 | 66 | 90 | 95 |
Qwen3-VL-4B
| Prompt Tokens | FP16 Prefill (tok/s) | W8A16 Prefill (tok/s) | W8A8 PC Prefill (tok/s) | FP16 Decode (tok/s) | W8A16 Decode (tok/s) | W8A8 PC Decode (tok/s) |
|---|---|---|---|---|---|---|
| 1334 | 1884 | 1786 | 2186 | 32 | 56 | 54 |
| 2393 | 1815 | 1700 | 2028 | 31 | 55 | 52 |
| 3455 | 1755 | 1603 | 1881 | 30 | 52 | 49 |
LLM 量化:精度与速度对比
| Models | Quantization Configuration | wikitext2 PPL(↓) | Prefill Time (s)(↓) | Peak Memory (GB)(↓) |
|---|---|---|---|---|
| Qwen3-8B | FP16 | 9.726 | 179.9 | 18.93 |
| W8A16 (mlx RTN) | 9.707 | 221.3 | 12.07 | |
| W8A8 (per-channel) | 9.756 | 123.5 | 11.32 | |
| W8A8 (per-group gs=64) | 9.744 | 179.1 | 11.83 | |
| W8A8 (per-group gs=128) | 9.727 | 165.8 | 11.61 | |
| Llama3-8B | FP16 | 6.138 | 175.8 | 18.32 |
| W8A16 (mlx RTN) | 6.147 | 236.9 | 11.46 | |
| W8A8 (per-channel) | 6.271 | 123.3 | 10.69 | |
| W8A8 (per-group, gs=64) | 6.269 | 178.7 | 11.19 | |
| W8A8 (per-group, gs=128) | 6.270 | 155.7 | 10.98 | |
说明
需要注意的是,cider 假定权重是 量化友好(quantization-friendly) 的。这意味着你需要确保模型已经经过某种训练后量化校准方法,例如 GTPQ、SmoothQuant 或 Quarot 等,以处理异常值(outliers),或者模型本身是通过 QAT(量化感知训练)训练的。当 cider 转换模型(例如使用 w8a8)并计算量化 scale 时,默认采用最简单的 min-max 方法,同时也支持第 99 百分位方法(clip_percentile=99.9)。如果遇到乱码输出,很大概率是模型受到了 异常值(outliers) 的影响。因此,某些预处理步骤是 必要的。
环境要求
- Apple M5+ 用于 INT8 TensorOps(M4 及以下:以纯 Python 方式安装,
is_available()返回 False) - Python 3.12+
- MLX >= 0.31
- nanobind >= 2.12(仅在 M5+ 上进行 C++ 编译时需要)
- CMake >= 3.27(仅在 M5+ 上进行 C++ 编译时需要)
安装
pip install -e .
在 M5+ 上,这会运行 CMake 编译 C++ 扩展,然后安装 Python 包。 在 M4 及以下设备上,仅安装 Python 包(不会出现编译错误)。
快速开始
一行命令完成模型转换(推荐)
from cider import convert_model, is_available
model, proc = load("path/to/model")
if is_available():
convert_model(model)
# CiderLinear auto-detects:
# seq_len > 1 → W8A8 INT8 TensorOps (faster prefill)
# seq_len == 1 → INT8 MV kernel (near-native decode speed)
else:
pass # Falls back to standard MLX inference on M4
重要提示 在量化视觉语言模型(Vision-Language Models,VLMs)时,视觉 Transformer(ViT)通常不会被替换。直接使用 convert_model 也会量化视觉模型的线性层,这通常会导致精度下降。对于 VLMs,我们建议调用 convert_model(model.language_model),仅对语言模型应用 GPTQ、SmoothQuant 和 AWQ 等现有量化方法。
已在选定的 MLX Transformer 模型上测试,包括 Qwen3、Qwen3-VL 和 Llama3 系列。其他架构可能需要适配。
层级别 API
import numpy as np
import mlx.core as mx
from cider import W8A8Linear, W4A8Linear, is_available
assert is_available(), "Requires Apple M5+"
# Prepare weight
W = np.random.randn(4096, 4096).astype(np.float16)
# W8A8 linear (per-channel)
from cider.ops import quantize_weight_int8
w_int8, scale = quantize_weight_int8(W)
layer = W8A8Linear(
w_int8=mx.array(w_int8), scale_w=mx.array(scale),
group_size=0, in_features=4096, out_features=4096
)
x = mx.random.normal((32, 4096)).astype(mx.float16)
y = layer(x) # lazy — builds MLX graph
mx.eval(y) # GPU executes
# W4A8 linear (half the weight memory)
layer4 = W4A8Linear.from_weights(W)
y4 = layer4(x)
mx.eval(y4)
底层 API
from cider import perchannel_linear, w4a8_linear, quantize_weight_int8, pack_weight_int4
# Quantize weights (numpy, offline)
w_int8, scale = quantize_weight_int8(W_np)
packed_w4, scale4 = pack_weight_int4(W_np)
# Primitive calls (return lazy mx.array)
y = perchannel_linear(x, mx.array(w_int8), mx.array(scale))
y4 = w4a8_linear(x, mx.array(packed_w4), mx.array(scale4))
项目结构
cider/
├── cider/ # Python package
│ ├── __init__.py # Public API (conditional on is_available)
│ ├── ops.py # Primitive wrappers + quantize helpers
│ ├── nn.py # CiderLinear, W4A8Linear (nn.Module)
│ ├── convert.py # convert_model() high-level API
│ ├── attention/ # Optimized SDPA
│ │ ├── __init__.py
│ │ └── sdpa.py # patch_sdpa / unpatch_sdpa / autotune_sdpa
│ └── kernels/ # Metal shaders (bundled)
│ ├── w8a8_matmul.metal # W8A8 GEMM (prefill, M>1)
│ ├── w8a8_int8_mv.metal # W8A8 per-channel MV (decode, M=1)
│ ├── w8a8_quantize.metal # Per-token activation quantization
│ ├── w4a8_matmul.metal # W4A8 GEMM (prefill)
│ ├── pergroup_int8_gemm.metal # Per-group GEMM (prefill)
│ ├── pergroup_int8_mv.metal # Per-group MV (decode)
│ ├── cider_sdpa_vector.h # SDPA kernel header (1-pass & 2-pass)
│ └── cider_sdpa_vector.metal # SDPA kernel template instantiations
├── csrc/ # C++ MLX primitives (nanobind, M5+ only)
│ ├── include/
│ │ ├── w8a8_primitive.h
│ │ ├── w4a8_primitive.h
│ │ ├── pergroup_primitive.h
│ │ └── sdpa_primitive.h
│ └── src/
│ ├── w8a8_primitive.mm
│ ├── w4a8_primitive.mm
│ ├── pergroup_primitive.mm
│ ├── sdpa_primitive.mm # SDPA 1-pass & 2-pass primitives
│ └── prim_bindings.cpp
├── benchmarks/
│ ├── bench_e2e_wxa16.py # End-to-end VLM benchmark (W8A8 + SDPA)
│ ├── bench_sdpa.py # SDPA micro-benchmark (correctness + speed vs MLX)
│ ├── bench_full.py # Isolated kernel latency (per-channel/per-group vs MLX)
│ ├── test_bitexact.py # Numerical correctness verification
│ └── mlx_native/ # MLX native INT8 comparison
├── tutorial/
│ ├── how_to_write_efficient_int_gemm_m5_en.md
│ └── how_to_write_efficient_int_gemm_m5_zh.md
├── tools/
│ ├── eval_ppl_all.py # Unified PPL eval (FP16/W8A16/per-channel/per-group)
│ ├── convert_compressed_tensors_to_mlx.py
│ └── smoothquant.py # SmoothQuant calibration
├── examples/
│ └── basic_usage.py
├── vlm_service/ # OpenAI-style VLM inference server
│ ├── server.py # FastAPI server (streaming + non-streaming)
│ ├── core_infer.py # HMInference engine (singleton)
│ ├── custom_qwen3vl.py # Custom Qwen3-VL generation loop
│ ├── config.py # Config loader
│ ├── bench_client.py # Server benchmark client
│ └── client.py # API client example
├── config/
│ └── config.yaml # Server & model configuration
├── experimental/ # ANE+GPU hybrid tensor parallelism (M4)
│ ├── split_linear.py # SplitLinear + ANEBridge + patch_model()
│ ├── bench.py # End-to-end benchmark
│ ├── libane_bridge_v6.m # ANE private API bridge (Obj-C source)
│ └── README.md
├── CMakeLists.txt
├── pyproject.toml
├── setup.py # Conditional build (M5+: full, M4: pure-Python)
└── README.md
VLM 推理服务
vlm_service/ 提供了一个开箱即用的 OpenAI 风格 VLM 推理服务器,支持 W8A8 加速。
快速开始
- 配置
config/config.yaml:
model_name_or_path: /path/to/your/model # MLX VLM model (e.g., Qwen3-VL-2B W8A16)
sampling:
max_new_tokens: 1024
temperature: 1.0
top_p: 1.0
server:
host: 0.0.0.0
port: 8341
ttl: 1800
w8a8:
mode: 'off' # 'auto' | 'on' | 'off'
auto:若硬件支持则启用 W8A8,否则回退到默认设置on:强制启用 W8A8(不支持时报错)。"选择 'on' 时,表示你的模型需要执行在线激活量化(online activation quantization)。在这种情况下,Cider 本身不保证量化精度,你需要自行应用某些量化算法,例如 SmoothQuant、QuaRot、GPTQ 或 QAT,以确保激活量化后精度不会明显下降。此选项只是让你在模型采用 W8A8 时能够利用硬件的计算优势,而不仅仅是模拟量化。"off:禁用 W8A8,使用标准 MLX 推理
- 启动服务器:
cd vlm_service
python server.py --config ../config/config.yaml
- 发送请求(OpenAI 风格 API):
# Text-only
curl http://localhost:8341/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "vlm",
"messages": [{"role": "user", "content": "Hello"}],
"stream": false
}'
# With image (base64)
curl http://localhost:8341/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "vlm",
"messages": [{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."} },
{"type": "text", "text": "What is in this image?"}
]}],
"stream": true
}'
API 端点
| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions |
POST | 对话补全(流式 / 非流式) |
/v1/models |
GET | 列出可用模型 |
/health |
GET | 健康检查 |
/v1/queue |
GET | 请求队列状态 |
服务中 W8A8 的工作方式
当 w8a8.mode 为 auto 或 on 时,服务器在启动时会调用 cider.convert_model(),将所有 Linear 层替换为 CiderLinear。推理过程中:
- Prefill(处理输入 token,seq_len > 1):使用 W8A8 INT8 GEMM 以加速计算
- Decode(逐 token 生成,seq_len == 1):使用 INT8 MV kernel(接近原生速度)
无需修改代码——切换会根据输入序列长度自动进行。
架构
MLX 自定义原语
W8A8Linear 与 W4A8Linear 均实现为 mlx::core::Primitive 的子类。这意味着:
- 惰性求值(lazy evaluation):
y = layer(x)构建图节点,而非立即计算 - 图组合:多个原语调用组合为单个 MLX 图
- 流调度:MLX 的调度器处理 GPU 派发顺序
Metal Kernel 流水线
每个原语都会派发 Metal 计算 kernel:
Prefill(M > 1):
- quantize_per_token:FP16 激活 → INT8 + 逐 token 缩放
- matmul_fused_dequant:INT8 × INT8 → INT32 → FP16(融合缩放反量化)
Decode(M = 1):
- int8_mv:直接 INT8 矩阵-向量乘积,权重反量化即时进行(无需激活量化)
对于 W4A8,GEMM 步骤在 fragment load 中包含内联 INT4→INT8 解包。
TensorOps matmul2d
INT8 GEMM 使用 Apple 的 mpp::tensor_ops::matmul2d(16, 32, 16)——通过 Metal 4 的 cooperative_tensor API,在 M5+ 上可用的硬件加速 INT8×INT8→INT32 矩阵乘法。这与 MLX 的 NAX 模板可用的硬件指令相同。Cider 的 kernel 在存储阶段加入融合反量化(INT32 × scales → FP16),避免额外的设备内存往返。详见 kernel comparison。
Tile 配置
| Config | BM | BN | BK | SK | Threads | Use When |
|---|---|---|---|---|---|---|
| Large | 128 | 128 | 512 | 32 | 512 | M > 64 |
| Small | 32 | 128 | 512 | 32 | 128 | M ≤ 64 |
根据 M 自动选择。包含 L2 cache swizzle 派发。
ANE+GPU 异构张量并行(实验性)
我们发现,在 Mac 上进行推理时,仅利用了 GPU 与 CPU 两个硬件计算单元,而 Mac 上的 ANE(Apple Neural Engine)计算单元处于空闲状态。我们将此视为潜在的优化机会。受 maderix/ANE, 启发,我们开展了 ANE+GPU 混合推理模式的实验工作。目前我们将该方案应用于张量并行计算。在 M4 芯片上,在仅同步的前向推理期间(MLX 原生使用称为惰性求值的技术以降低同步开销;在端到端测试中,混合推理目前尚未显示出优势,主要因为我们尚未基于 MLX 的惰性求值实现该方案——这仍是后续工作),与纯 GPU 推理在同步流水线(synchronize pipeline)下相比,我们观察到约 3%~16% 的性能提升。这仍属探索性工作,端到端收益目前受限于缺少与惰性求值兼容的实现。
在 LLM prefill 阶段,GPU 的矩阵单元已满载——但 Apple Neural Engine 完全空闲。ANE Split 利用这一点,沿输出通道拆分每个线性层的 GEMM:
- ANE 计算约 65% 的输出通道(FP32,通过逆向工程的私有
_ANEClientAPI) - GPU 计算剩余约 35%(FP16,标准 MLX matmul)
- 两者并发运行,结果拼接
这是一种异构张量并行——不是数据并行,也不是流水线并行——利用同一 SoC 上的两个不同计算单元。
性能(Apple M4,Qwen3-VL-2B Prefill)
| seq | W8A16 GPU | SplitLinear | Speedup vs W8A16 |
|---|---|---|---|
| 512 | 639.9 ms | 615.9 ms | 1.039× |
| 1024 | 1348.6 ms | 1156.9 ms | 1.17× |
在测试的基准案例中,余弦相似度接近 1.0,top-1 token 一致率为 100%。
关键设计选择
- 仅 Prefill:Decode 回退到原始 GPU linear(零开销)
- 共享输入准备:Q/K/V 与 Gate/Up 投影通过
_InputGroup共享单次输入转置+numpy 拷贝 - 自动路由:Down 投影(IC > 2×OC)在 ANE 效率低下时保持仅 GPU
- 短序列绕过:序列 < 192 token 时跳过分拆(开销大于收益)
完整文档、用法、构建说明与限制见 experimental/README.md。
注意: ANE Split 已在 M4 上测试。M5 引入了 ANE 架构变更,可能破坏私有 API 桥接——尚未在 M5 上验证。
量化
| Component | Scheme | Granularity |
|---|---|---|
| W8A8 weights | Symmetric INT8 | Per-channel or per-group (gs=64/128) |
| W4A8 weights | Symmetric INT4 (zp=8) | Per-column |
| Activations | Symmetric INT8 | Per-token |
| Accumulation | INT32 | — |
| Output dequant | C_fp16 = C_int32 * s_act * s_weight |
Per-element |
限制
- M=1 单算子:逐通道 MV kernel 在独立 decode 调用时慢于 MLX W4A16。逐组 MV kernel 在端到端基准中与 MLX W8A16 decode 速度相差在 5% 以内。
- INT8 TensorOps 仅支持 Apple M5+:M4 及以下可安装但
is_available()返回 False。 - W4A8 慢于 W8A8:INT4→INT8 解包 ALU 开销(Metal 4 matmul2d 无原生 INT4 操作数)。
工具
统一 PPL 评估
# Run all 5 configurations in one script
python tools/eval_ppl_all.py --num-samples 50
# Evaluates: FP16, W8A16 (MLX native), W8A8 per-channel, per-group(gs=64), per-group(gs=128)
# Outputs comparison table at the end
路线图
- 一行式模型转换 API(
convert_model,自动 prefill/decode) - 自动 dtype 处理(float16 / bfloat16)
- 逐通道与逐组 W8A8 量化
- 专用 decode MV kernel(匹配原生 MLX 速度)
- 条件编译(M4 优雅回退)
- mlx_vlm 与 mlx_lm 集成示例
- 优化的 SDPA decode kernel(受 FlashInfer 启发,支持 GQA)
- 一行式
patch_sdpa(),透明加速 decode - SDPA AutoTune(按架构块大小扫描)
- ANE 原语惰性求值
- 集成剪枝功能
- KVCache 量化
作者
Multimodal Team, Mininglamp Technology
如有 bug 报告、功能请求或使用问题,请在本仓库提交 issue。
引用
若您认为本工作有价值,请引用:
@software{wang2026cider,
author = {Multimodal Team, Mininglamp Technology},
title = {Cider: Exploiting Unused INT8 TensorOps for Faster LLM Prefill on Apple Silicon},
year = {2026},
howpublished = {https://github.com/Mininglamp-AI/cider}
}
许可证
MIT
致谢
- MLX by Apple — primitive API, NAXFrag kernel architecture
- FlashInfer — inspired contiguous chunk GQA scheduling and register tiling for SDPA decode kernel
- Metal 4 MetalPerformancePrimitives for INT8 TensorOps
- maderix/ANE — inspired and informed our ANE+GPU tensor-parallel implementation