Tool

Run transformer inference 4.76x faster on Apple Silicon Neural Engine

Compiles MIL programs directly to Apple Neural Engine silicon, bypassing CoreML - up to 4.76x faster decode with full ANE training support.

Works with coremlswift

91
Spark score
out of 100
Updated 27 days ago
Source checked Sep 10, 2026
Version 0.9.0

Add to Favorites

Why it matters

Accelerate transformer model inference on Apple Silicon by compiling MIL programs directly to the Neural Engine through reverse-engineered private APIs, bypassing CoreML overhead for production-grade speed improvements in on-device AI applications.

Outcomes

What it gets done

01

Compile transformer models directly to ANE silicon with zero-copy IOSurface buffers

02

Fuse multi-layer transformer kernels into 2 ANE dispatches instead of 6

03

Generate tokens at 1.08 ms/token with verified two-token decode steps

04

Train models on ANE with forward and backward passes plus gradient accumulation

Source

Get it from source

Spark does not host a copy of it.

Open source

Reports

Agent outcome reports

No reports yet

Overview

Espresso

Espresso compiles MIL programs directly to Apple's Neural Engine via reverse-engineered private APIs, bypassing CoreML for a 4.76x faster decode (1.08ms/token vs 5.09ms/token) using fused multi-layer kernels and zero-copy IOSurface I/O. It also supports full ANE training. Use it on Apple Silicon (M1+, macOS 15+) for high-throughput on-device inference or training in internal tools, research, or enterprise apps; it relies on private APIs so it cannot ship on the App Store.

What it does

Espresso compiles MIL (Model Intermediate Language) programs straight to Apple Neural Engine (ANE) silicon through reverse-engineered private APIs (_ANEClient, _ANEInMemoryModel), bypassing CoreML entirely. There is no per-token recompilation: the decode loop compiles once and reuses the program across all steps, with the KV cache living directly in IOSurface buffers instead of being marshaled through CoreML, and each step producing two exact tokens with verified parity. Fused triplet kernels process 3 transformer layers per ANE dispatch, so a 6-layer model runs in 2 dispatches instead of 6.

On a 6-layer reference model (dim=768, 12 heads, 32k vocab, seqLen=256, M3 Max, macOS 15), Espresso decodes at 1.08 ms/token (926 tok/s) versus CoreML's .cpuAndNeuralEngine path at 5.09 ms/token (196 tok/s) - a 4.76x speedup - and roughly 11x faster than llama.cpp's Metal GPU path, which has no ANE backend at all. Beyond inference, Espresso supports full training on the ANE: forward and backward passes with gradient accumulation and Adam. The whole project is pure Swift 6.2 with ~Copyable move-only tensors, strict concurrency, typed throws, and zero external dependencies - only Apple system frameworks.

Espresso also ships an ESP model platform around portable .esp bundles: .esp is the canonical portable model bundle, .espc is a derived compiled-cache layer, espc packs native model directories into .esp, and esprun inspects, resolves, and runs bundle artifacts.

When to use - and when NOT to

Use Espresso when you're building on Apple Silicon (M1 or later, macOS 15+) and want transformer inference or training to run directly on the Neural Engine at meaningfully higher throughput than CoreML's standard ANE path, or when you're researching or building internal tools, sideloaded apps, or enterprise-distributed software that can rely on private Apple APIs. It's a fit for anyone who wants zero-copy IOSurface I/O, fused multi-layer kernel dispatch, and a Swift Package Manager-integrable ANE runtime.

Do not use it in anything destined for the App Store: apps using the private _ANEClient and _ANEInMemoryModel APIs will be rejected. It also has no support for Intel Macs (no Neural Engine) and only conditional, entitlement-gated support on iOS/tvOS (not App Store safe there either). The published benchmarks are run on a local artifact family built by this repo, not a pretrained production model, and results are explicitly hardware- and OS-dependent since the whole approach relies on undocumented, reverse-engineered private APIs.

Inputs and outputs

Quick start clones the repo and runs a bootstrap script that builds, downloads demo weights, and launches a TUI:

git clone https://github.com/christopherkarani/Espresso.git
cd Espresso
./espresso

Other CLI entry points include ./espresso "Hello" (generate text), ./espresso doctor (check host readiness), ./espresso compare --no-power "Hello" (side-by-side vs CoreML), ./espresso install (install to ~/.local/bin), swift run espresso-bench --ane-only --inference --layers 6 (benchmarking), and the espc/esprun tools for packing and running .esp model bundles.

For library integration, add the package via Swift Package Manager and compile a kernel directly:

import ANERuntime

let kernel = try ANEKernel(milText: myMIL, weights: blobs, inputSizes: [input], outputSizes: [output])
try kernel.eval()
let result = kernel.outputSurface(at: 0)

Output is a zero-copy IOSurface-backed result read directly off the Neural Engine, plus, for benchmarking, machine-readable results written to artifacts/benchmarks/.

Integrations

Espresso integrates as a Swift Package Manager dependency, exposing the ANERuntime and ANETypes products for direct use in a Swift project. Internally it's layered as ANEInterop (a dlopen bridge to the private ANE APIs with NEON-vectorized I/O), ANETypes (~Copyable tensors and IOSurface I/O), MILGenerator (28+ kernel variants for forward, backward, decode, and fused kernels), CPUOps (RMSNorm, RoPE, embedding, softmax, and Adam via Accelerate/vDSP), and ANERuntime (MIL-to-ANE-E5-binary compilation and IOSurface buffer management), with Espresso itself providing transformer layers, generation, and the training loop, plus espresso-train and espresso-bench CLIs. Testing integrates swift test for unit tests plus opt-in hardware and cross-validation test suites gated by ANE_HARDWARE_TESTS=1 and OBJC_CROSS_VALIDATION=1.

Who it's for

Swift developers and ML engineers building on-device transformer inference or training for Apple Silicon who need throughput beyond CoreML's standard ANE path and are comfortable relying on private, reverse-engineered Apple APIs outside the App Store - internal tools, research, sideloaded apps, or enterprise distribution. It is licensed under MIT.

Source README

Espresso

Direct Neural Engine inference for transformers on Apple Silicon.

CI ANE Matrix Swift 6.2 License: MIT macOS 15+ Latest Release


Espresso compiles MIL programs straight to ANE silicon through reverse-engineered private APIs (_ANEClient, _ANEInMemoryModel). No CoreML in the hot path. No per-token recompilation. IOSurface buffers and fused multi-layer kernels for Apple Silicon.

  • Direct ANE path - private-API compile once, reuse across decode steps
  • Fused multi-layer kernels - fewer ANE dispatches per token
  • Zero-copy I/O - NEON-vectorized surface reads, vDSP/Metal where useful
  • Pure Swift 6.2 core - ~Copyable move-only tensors, strict concurrency, typed throws
  • Zero required third-party packages - clean clone resolves and builds on macOS 15+

Espresso generating tokens on ANE

Product journeys

Pick one path. Everything else is optional or research.

1. Try it (demo)

git clone https://github.com/christopherkarani/Espresso.git
cd Espresso
./espresso doctor   # host readiness check (scripts, ANE, Python)
./espresso prepare  # bootstrap GPT-2 demo weights + tokenizer (network; torch/transformers)
./espresso          # builds if needed, launches the GPT-2 TUI demo

First demo run also bootstraps assets automatically when they are missing. That step needs
network access and a Python with torch + transformers (Espresso can create a managed venv).

2. Serve a model (.esp bundles)

Portable model bundles are the retained serving path:

# Pack a prepared native model directory into a portable bundle
swift run espc pack-native /path/to/model /tmp/model.esp --overwrite

# Inspect / run
swift run esprun inspect /tmp/model.esp
swift run esprun generate /tmp/model.esp "Hello" 32

# Same bundle boundary via the generate CLI
swift run espresso-generate generate --bundle /tmp/model.esp --max-tokens 32 "Hello"
Artifact Role
.esp Canonical portable model bundle
.espc Derived compiled-cache layer (host-local)
espc Pack native model dirs into .esp
esprun Inspect, resolve, generate from bundles
espresso-generate --bundle Full generate/benchmark CLI on the same boundary

3. Chat with a real open-weight model (Qwen2.5-1.5B-Instruct)

# Convert the local Hugging Face snapshot (or download it) and pack a .esp bundle
python3 scripts/convert_qwen25_05b_to_esp.py --model Qwen/Qwen2.5-1.5B-Instruct

# Multi-turn chat. Fallback is disabled. Live tok/s, TTFT, and J/tok stay in the TUI.
./espresso chat --model ~/Library/Caches/Espresso/qwen25-15b/Qwen2.5-1.5B-Instruct.esp

Qwen2.5-1.5B-Instruct decodes through Espresso's ANE hybrid path: Q/K/V and the SwiGLU
FFN on the Neural Engine; RoPE, attention, and the ~467 MB LM head on the CPU by design
(cpu_fp16_tiled). Chat keeps Qwen Instruct history across turns. Commands: /reset
/retry /exit. Ctrl-C cancels the current completion.

Throughput and energy are live footer measurements for that completion, not README
headlines. Optional dual-pane vs MLX (same checkpoint, greedy, fp16 vs fp16; compile
excluded from tok/s):

./espresso chat --vs mlx --greedy --model ~/Library/Caches/Espresso/qwen25-15b/Qwen2.5-1.5B-Instruct.esp

See docs/qwen15b-parity.md for the 1.5B greedy contract and
the commands that regenerate it.

4. Reproduce 0.5B greedy parity

# Default converter target remains Qwen2.5-0.5B-Instruct
python3 scripts/convert_qwen25_05b_to_esp.py

ESPRESSO_REALMODEL_DISABLE_HYBRID_FALLBACK=1 \
  ./espresso generate --model ~/Library/Caches/Espresso/qwen25-05b/Qwen2.5-0.5B-Instruct.esp \
  -n 24 "The capital of France is"

Qwen2.5-0.5B-Instruct is the parity/repro path. It reproduces a PyTorch fp32 reference
on a fixed 12-prompt greedy suite: 10 of 12 sequences match token-for-token, 341 of
384 tokens agree
. That 10/12 is generate-path evidence (cpu_fp16_tiled LM head). A
separate probe - chained per-layer hidden states plus a NumPy/Python LM head, not the
served tiled classifier - agrees with PyTorch to 9.3e-5 in logits on the fp32 CPU
stack through all 24 layers, and to ~0.96 on the ANE hybrid stack. The two greedy
divergences come from fp16 execution on the ANE (up to ~1 logit of error) and both land
on precisely the reference's runner-up token at top-1/top-2 gaps of 0.027 and 0.069.
That is one model, greedy, fp16, measured - not a general-model claim, and not a speed
claim.
See docs/qwen-parity.md for the per-layer report, the exact
commands, and every ANE limitation hit along the way.

5. Embed the library (ANEKernel)

// Package.swift
.package(url: "https://github.com/christopherkarani/Espresso.git", from: "0.9.0")

import ANERuntime

let kernel = try ANEKernel(
    milText: myMIL,
    weights: blobs,
    inputSizes: [input],
    outputSizes: [output]
)
try kernel.eval()                         // runs on Neural Engine
let result = kernel.outputSurface(at: 0)  // zero-copy read

For end-to-end text generation from prepared weights, use RealModelInference or a .esp bundle rather than hand-writing MIL.

Optional tooling (bench, install, training)
./espresso install                            # PATH shim → this checkout (does not download weights)
./espresso prepare                            # download/convert GPT-2 demo assets
./espresso compare --no-power "Hello"         # side-by-side vs CoreML (demo weights)
swift run espresso-bench --ane-only --inference --layers 6
swift run espresso-train                      # experimental ANE training loop

Rejected experiment configs and distillation scripts live under research/ and are not product entry points.

Benchmark

Numbers below match the checked-in machine-readable results in
benchmarks/results/latest.json
(M3 Max, macOS 15.0, Espresso 1.1.0).

Espresso vs CoreML (local 6-layer Stories artifact)

Backend ms/token tok/s Notes
Espresso ANE (recurrent fused, 6-layer) 1.93 519 Fused 3-layer recurrent decode + ANE classifier
Espresso ANE (direct transformer, 6-layer) 6.56 153 Same model without recurrent fusion
CoreML .cpuAndNeuralEngine 6.58 152 Apple's standard ANE path
Espresso speedup vs CoreML 3.41× fused recurrent path

All Espresso / CoreML numbers: 6-layer local artifact · dim=768 · 12 heads · 32k vocab · seqLen=256 · M3 Max · macOS 15.
This is a research / demo artifact family, not a pretrained production model.

What these numbers are not

  • Not a claim about full GPT-2 117M or llama.cpp Metal on the same workload
  • Not every serving path: retained exact hybrid .esp Stories runs can land lower in wall-clock tok/s after compile and full decode accounting
  • Not trunk-only or partial-pipeline peaks from blog posts - only figures backed by latest.json (or a PR artifact from the reproduce script) are project claims
Reproduce Espresso benchmarks
RESULTS_DIR=results/$(date +%Y%m%d-%H%M%S) \
REPEATS=5 WARMUP=3 ITERATIONS=20 \
./scripts/reproduce_local_real_artifact_claim.sh

Machine-readable output lands in artifacts/benchmarks/ and is kept out of git.
Update benchmarks/results/latest.json only when you intentionally refresh the public table.
CI fails if the README table drifts from latest.json.

Platform Compatibility

SoC Neural Engine Tested Notes
M1 / M1 Pro / M1 Max / M1 Ultra 16-core ANE Full feature set
M2 / M2 Pro / M2 Max / M2 Ultra 16-core ANE Full feature set
M3 / M3 Pro / M3 Max 18-core ANE Reference hardware (M3 Max)
M4 / M4 Pro / M4 Max 38-core ANE Faster compile cache warm-up
Intel Mac - No Neural Engine
Apple A-series (iOS) ⚠️ Requires entitlement; not App Store safe

macOS 15+ required. iOS / tvOS not supported out of the box (private API entitlements differ per platform).

How It Works

                    ┌─────────────────────┐
                    │   MIL Program Text   │  Generated per-kernel
                    └──────────┬──────────┘
                               ▼
                    ┌─────────────────────┐
                    │  _ANEClient compile  │  Private API (dlopen)
                    └──────────┬──────────┘
                               ▼
                    ┌─────────────────────┐
                    │    ANE E5 Binary     │  Cached by system
                    └──────────┬──────────┘
                               ▼
              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
     ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
     │  IOSurface   │ │  IOSurface   │ │  IOSurface   │
     │   (input)    │ │  (weights)   │ │  (output)    │
     └──────┬───────┘ └──────────────┘ └──────┬───────┘
            │          ANE Hardware            │
            └──────────────eval───────────────┘

The decode loop compiles once and reuses the program across all steps. KV cache lives in IOSurface buffers - not marshaled through CoreML.

Architecture

ANEInterop (ObjC/C — private API bridge)
  └── ANETypes (~Copyable value types, IOSurface I/O)
          ├── MILGenerator (kernel variants)
          │       └── ANERuntime (compile, eval, surface management)
          │               └── Espresso / RealModelInference (serving, decode)
          │                       ├── espresso-generate / esprun (CLI)
          │                       └── ESPBundle (portable .esp)
          └── CPUOps (Accelerate/vDSP kernels)
Module What it does
ANEInterop dlopen bridge to _ANEClient and _ANEInMemoryModel. NEON-vectorized I/O.
ANETypes ~Copyable tensors, SurfaceIO, weight serialization, model config.
MILGenerator Generates MIL text for forward, backward, decode, and fused kernels.
CPUOps RMSNorm, RoPE, embedding, softmax, Adam via Accelerate/vDSP.
ANERuntime Compiles MIL to ANE E5 binaries. Manages IOSurface buffers and compile budget.
Espresso Generation harnesses, decode, training experiments.
RealModelInference Hybrid serving path used by espresso-generate / .esp runtime.
ESPBundle / ESPRuntime Portable .esp bundles and runtime resolution.

SPM Integration

// Package.swift
dependencies: [
    .package(url: "https://github.com/christopherkarani/Espresso.git", from: "0.9.0")
],
targets: [
    .target(name: "MyApp", dependencies: [
        .product(name: "ANERuntime", package: "Espresso"),
        .product(name: "ANETypes",   package: "Espresso"),
    ])
]
import ANERuntime
import ANETypes

let kernel = try ANEKernel(
    milText: milText,
    weights: weightBlobs,
    inputSizes: [inputByteSize],
    outputSizes: [outputByteSize]
)
try kernel.eval()
let output = try kernel.outputSurface(at: 0)

Dependencies

Zero third-party Swift packages. The package graph depends only on Apple system frameworks (Foundation, Accelerate, IOSurface, Metal, CoreML). A clean clone of this repo alone must resolve and build.

Requirements

Minimum
Hardware Apple Silicon (M1+) with Neural Engine
macOS 15.0+
Swift 6.0+ (6.2 recommended)
Dependencies None required - Apple system frameworks only

Testing

swift test                                                    # unit tests (no ANE needed)
ANE_HARDWARE_TESTS=1 swift test --filter "ANERuntimeTests|EspressoTests"  # hardware tests
OBJC_CROSS_VALIDATION=1 ANE_HARDWARE_TESTS=1 swift test --filter CrossValidationTests  # parity

CI runs non-hardware unit tests including ESP bundle/runtime and RealModelInference unit suites, asserts a zero-dependency default graph, and checks README claim numbers against latest.json. Hardware ANE tests run on self-hosted matrix jobs.

Research vs product

Path Location
Retained product surface Sources/, ./espresso, espc / esprun / espresso-generate, public docs
Quarantined experiments research/ - rejected draft/student/future-head configs and tooling

Do not treat research/ numbers or flags as supported product behavior.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.