# ANE Split — ANE+GPU Tensor Parallelism for Prefill Acceleration *Status: experimental research prototype. Uses private ANE APIs and is not part of Cider's stable inference path.* Split each linear layer's GEMM along output channels: **ANE** and **GPU** computes running concurrently. This explores whether the otherwise idle Apple Neural Engine can help accelerate prefill, with minimal degradation observed in our current tests. > **Platform:** Apple M4 (tested). M5 ANE API changes may cause failures — not yet validated. ## How It Works ``` Input x ──┬── ANE (r output channels, FP32, private API) ──┐ │ ├── concat → output └── GPU (1 - r output channels, FP16, MLX matmul) ──┘ ``` 1. **SplitLinear** wraps each `nn.Linear` / `nn.QuantizedLinear` 2. In **prefill mode** (seq ≥ 192): split path with ANE+GPU concurrency 3. In **decode mode**: falls back to original `nn.Linear` on GPU (zero overhead) 4. Same-input projections (Q/K/V, Gate/Up) share input preparation via `_InputGroup` ### Automatic Layer Routing | Layer Type | Routing | Reason | |------------|---------|--------| | Q, K, V, O projections | ANE+GPU split | Expand: IC → OC, ANE efficient | | Gate, Up projections | ANE+GPU split | Expand: IC → OC, ANE efficient | | Down projection | GPU only | Narrow: IC > 2×OC, ANE inefficient | | Short sequences (< 192) | GPU only | Split overhead > benefit | ## Performance (Apple M4, Qwen3-VL-2B) | 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×** | - Accuracy: cos ≈ 1.0, top-1 match = 100% - 168 layers split (28 layers × 6 projections), 28 GPU-only (down_proj) ## Quick Start ```python from split_linear import patch_model, SplitLinear # Load any MLX VLM model from mlx_vlm.utils import load as vlm_load model, processor = vlm_load("path/to/model") # Patch all linear layers (one-liner) bridge = patch_model(model, seq=512) # Enable split for prefill, disable for decode SplitLinear.set_prefill(True) # prefill: ANE+GPU parallel # ... run prefill ... SplitLinear.set_prefill(False) # decode: original GPU path ``` ## Benchmark ```bash # Default: seq=512 python3 bench.py # Custom seq length python3 bench.py 1024 # Custom model path MODEL_PATH=/path/to/model python3 bench.py 512 ``` ## Building the ANE Bridge To build libane_bridge_v6.dylib from the source: ```bash clang -shared -O2 -framework Foundation -framework CoreML \ -framework Accelerate -o libane_bridge_v6.dylib libane_bridge_v6.m ``` > Requires macOS with ANE private frameworks. Uses undocumented `_ANEClient` API. ## Limitations - **M4 only** — M5 ANE internal changes may break the private API bridge - **Fixed sequence length** — ANE models are compiled for a specific seq; re-patch needed for different lengths - **FP32 on ANE** — ANE operates in FP32 (no INT8/FP16 GEMM); benefit comes from parallelism, not precision - **Memory overhead** — ANE models consume additional system memory (~200MB for 2B model) - **No decode benefit** — Decode is single-token, falls back to GPU (no split overhead) - **No E2E benefit** - MLX natively employs lazy evaluation to reduce synchronization overhead. In end-to-end testing, our hybrid approach currently shows no advantage because we haven't yet implemented it with MLX's lazy evaluation. ## License MIT