Espresso ejecuta transformers en el Neural Engine del Mac sin pasar por Core ML
Índice de contenidos
- Puntos clave
- Qué es Espresso y qué hace distinto
- Cómo funciona Espresso por dentro
- Cuánto más rápido es Espresso que Core ML
- Qué pasa con un modelo real como Qwen2.5
- Cómo probar Espresso en tu Mac
- Riesgos de usar las APIs privadas del Neural Engine
- Espresso frente a Core ML, MLX y llama.cpp
- Preguntas frecuentes
- ¿Espresso funciona en un Mac con Intel?
- ¿Puedo usar Espresso en una app de la App Store?
- ¿Es más rápido que MLX para chatear con un LLM?
- Conclusión
- Fuentes
Espresso es una biblioteca en Swift que compila programas MIL directamente para el Neural Engine de los Mac con Apple Silicon mediante APIs privadas, sin Core ML. En un modelo de 6 capas en un M3 Max genera 519 tokens por segundo frente a 152 de Core ML; con modelos reales como Qwen2.5 la atención sigue en la CPU.
Espresso es una biblioteca de código abierto en Swift que ejecuta transformers en el Neural Engine (ANE) de los Mac con Apple Silicon sin usar Core ML. Genera el programa de cada capa en MIL, el lenguaje intermedio de Apple, y lo compila con dos clases privadas de macOS que su autor, Christopher Karani, ha documentado por ingeniería inversa. Aquí tienes qué hace distinto, cómo funciona, qué miden sus cifras frente a Core ML, qué pasa con un modelo real como Qwen2.5 y qué riesgos tiene usar APIs privadas. No lo hemos ejecutado en un Mac propio: todo sale del repositorio y de su historial, revisados el 25 de octubre de 2026. Hay una versión en inglés de este artículo.
Puntos clave
- Espresso compila programas MIL para el ANE con las clases privadas
_ANEClienty_ANEInMemoryModel, una vez por modelo, y guarda la caché KV en buffers IOSurface compartidos con la CPU. - En un modelo de prueba de 6 capas en un M3 Max, la ruta fusionada de Espresso tarda 1,93 ms por token (519 tokens/s) y Core ML tarda 6,58 ms (152 tokens/s): 3,41 veces más.
- Con Qwen2.5-0.5B, 10 de 12 secuencias y 341 de 384 tokens coinciden con la referencia de PyTorch, pero la atención, RoPE y la capa de salida corren en la CPU, y el proyecto no da cifras de velocidad.
- La capa de salida de Qwen2.5-0.5B ocupa 272 MB y la memoria SRAM disponible en el ANE ronda los 32 MB, así que no cabe.
- Una app que use APIs privadas de Apple no entra en la App Store, y una actualización de macOS puede romperla.
Qué es Espresso y qué hace distinto
Espresso es un paquete de Swift 6.2 con licencia MIT que habla con el Neural Engine sin Core ML de por medio. El README de Espresso[1] lo resume así: "No CoreML in the hot path. No per-token recompilation." El proyecto nació el 4 de marzo de 2026, tiene 392 commits, 173 estrellas y una versión publicada, la v0.9.0, del 12 de agosto.
El Neural Engine es el acelerador de redes neuronales que Apple integra en sus chips junto a la CPU y la GPU. Apple lo presenta como "the energy-efficient and high-throughput engine for ML inference on Apple silicon". Su guía para desplegar transformers en el Neural Engine[2] da su pico en FP16: 0,6 TFLOPS en el A11 de 2017 y 15,8 TFLOPS en el ANE de 16 núcleos de 2021. La única vía pública para usarlo es Core ML, y Core ML decide en tiempo de ejecución qué operaciones van al ANE y cuáles a la CPU.
Las herramientas habituales para ejecutar LLM en un Mac, como oMLX u Ollama con MLX, usan la GPU con Metal. Espresso apunta al ANE, que consume menos energía, y lo hace sin el envoltorio de Core ML. La biblioteca no depende de paquetes de terceros: solo de Foundation, Accelerate, IOSurface, Metal y Core ML.
Cómo funciona Espresso por dentro
Espresso sustituye la compilación de Core ML por la suya propia y reutiliza el programa compilado en cada token. Según el README, la secuencia es esta:
- El módulo
MILGeneratorescribe en texto el programa MIL de cada kernel, con variantes que fusionan hasta tres capas en un solo envío al ANE ANEInteropcarga condlopenlas clases privadas_ANEClienty_ANEInMemoryModely compila ese texto a un binario del ANE, que el sistema guarda en caché- Las entradas, los pesos y las salidas viven en buffers IOSurface, memoria que comparten la CPU y el ANE, así que leer un resultado no exige copiarlo
- El bucle de generación compila una vez y repite la evaluación en cada paso, con la caché KV también en IOSurface
Otros proyectos han seguido el mismo camino. El artículo Orion, publicado en arXiv el 6 de marzo de 2026[3], compila MIL y lo evalúa con _ANEClient. Cataloga 20 restricciones del ANE (14 sin documentar hasta entonces) y genera más de 170 tokens/s con GPT-2 124M en un M4 Max.
El repositorio ane de mdaiter[4] documenta que, sin los permisos adecuados, _ANEClient se crea pero compileModel: devuelve NULL sin error. Espresso usa la ruta de modelo en memoria y declara funcionar en macOS 15 sin permisos especiales; en iOS sí los necesita.
Cuánto más rápido es Espresso que Core ML
Espresso genera 3,41 veces más tokens por segundo que Core ML en la única prueba que publica, y esa prueba usa un modelo de juguete. El fichero latest.json de los benchmarks[5] es del 17 de marzo de 2026. Mide un modelo de 6 capas tipo Llama entrenado con TinyStories (dimensión 768, 12 cabezas, vocabulario de 32 000 tokens, secuencia de 256) en un M3 Max con macOS 15.0:
El gráfico deja ver de dónde sale la ganancia. Sin fusionar capas, Espresso rinde lo mismo que Core ML (153 frente a 152 tokens/s). La ventaja aparece con la ruta que fusiona tres capas en cada envío al ANE y calcula también en el ANE la capa de clasificación final.
El propio README acota sus cifras: no son una comparación con GPT-2 de 117 millones de parámetros ni con llama.cpp sobre Metal, y el modelo es "a research / demo artifact family, not a pretrained production model". Un detalle que da confianza es que la integración continua del proyecto falla si la tabla del README deja de coincidir con latest.json.
Qué pasa con un modelo real como Qwen2.5
Con modelos de Hugging Face, Espresso reparte el trabajo entre el ANE y la CPU, y su documentación evita dar cifras de velocidad. El informe de paridad de Qwen2.5-0.5B[6] detalla qué corre dónde:
| Etapa | Dónde corre | Motivo |
|---|---|---|
| Proyecciones Q, K y V | ANE | Un kernel por capa |
| Red feed-forward SwiGLU | ANE | Kernels fusionados |
| RoPE, softmax de atención y caché KV | CPU | Trabajo secuencial ligado a la caché |
| Capa de salida (151 936 × 896) | CPU | 272 MB frente a unos 32 MB de SRAM en el ANE |
Con esa división, 10 de 12 secuencias de prueba y 341 de 384 tokens coinciden con PyTorch en FP32 con decodificación voraz. Las dos secuencias que se desvían lo hacen por la precisión FP16 del ANE, con errores de hasta un logit, y en los dos casos eligen el segundo token de la referencia. El documento lo cierra así: "That is one model, greedy, fp16, measured — not a general-model claim, and not a speed claim."
El informe de Qwen2.5-1.5B[7] añade las limitaciones del hardware que se encontró el autor:
- Presupuesto de compilación: cada proceso solo puede compilar un número finito de programas para el ANE, y 720 compilaciones lo agotarían
- Anchos de capa: Qwen3-0.6B acumuló 156 reintentos y 195 fallos de compilación; Qwen2.5-0.5B, con todas sus dimensiones múltiplos de 64, compila sus 24 capas sin fallos en unos 20 s
- Desbordamiento en FP16: en el modelo de 1,5B, la media de los cuadrados de RMSNorm desborda FP16 cuando el RMS residual pasa de 256, y Espresso escala el residuo por 1/4 antes de elevarlo
- Capa de salida: en el modelo de 1,5B ocupa unos 467 MB y también se queda en la CPU
Cómo probar Espresso en tu Mac
La demo se instala con tres órdenes y necesita un Mac con Apple Silicon, macOS 15 o superior y Swift 6.0 o superior. Estos son los pasos del README a 25 de octubre de 2026:
git clone https://github.com/christopherkarani/Espresso.git
cd Espresso
./espresso doctor
./espresso prepare
./espresso
doctor comprueba que el equipo tiene ANE, los scripts y un Python utilizable. prepare descarga los pesos de demostración de GPT-2 y el tokenizador, para lo que necesita red y un Python con torch y transformers (Espresso puede crear su propio entorno virtual). La última orden compila el proyecto si hace falta y abre la demo de texto en la terminal.
Para conversar con un modelo real, el README convierte Qwen2.5-1.5B-Instruct al formato .esp del proyecto y abre un chat:
python3 scripts/convert_qwen25_05b_to_esp.py \
--model Qwen/Qwen2.5-1.5B-Instruct
./espresso chat --model \
~/Library/Caches/Espresso/qwen25-15b/Qwen2.5-1.5B-Instruct.esp
El script de conversión descarga el modelo de Hugging Face si no lo tienes y empaqueta los pesos. El chat muestra en el pie los tokens por segundo, el tiempo hasta el primer token y los julios por token de cada respuesta. Con --vs mlx --greedy abre un segundo panel con MLX y el mismo modelo, que es la comparación que te interesa hacer en tu propia máquina.
Riesgos de usar las APIs privadas del Neural Engine
El riesgo principal de Espresso es el mismo que le da su ventaja: depende de clases que Apple no documenta ni garantiza. El README lo dice sin rodeos: "Apps using private ANE APIs (_ANEClient, _ANEInMemoryModel) will be rejected." Estas son las consecuencias prácticas:
- App Store: una app que enlace estas clases será rechazada en la revisión de Apple
- Actualizaciones de macOS: Apple puede cambiar o retirar las clases privadas en cualquier versión sin aviso
- iOS y tvOS: no funcionan tal cual, porque los permisos de las APIs privadas cambian por plataforma
- Precisión: el ANE calcula en FP16, y en modelos grandes los errores se acumulan capa a capa
- Madurez: la última versión publicada es la v0.9.0, pero la tabla de benchmarks cita una "Espresso 1.1.0" que no existe como versión
Hay otro detalle que conviene corregir si lees el README. Su tabla de compatibilidad atribuye al M3 un "18-core ANE" y al M4 uno de "38-core". Son cifras de rendimiento en billones de operaciones por segundo (TOPS) que Apple publicó para esos chips, no núcleos, y el propio latest.json del proyecto describe el ANE del M3 Max como de 16 núcleos.
Espresso frente a Core ML, MLX y llama.cpp
Espresso no compite hoy con las herramientas de GPU para ejecutar LLM grandes; es un proyecto de investigación sobre el ANE. Esta tabla resume las diferencias:
| Criterio | Espresso | Core ML | MLX y llama.cpp |
|---|---|---|---|
| Unidad de cálculo | ANE, con atención en CPU | La que elija Core ML | GPU con Metal |
| API | Privada de Apple | Pública | Pública |
| Apta para la App Store | No | Sí | Sí |
| Modelos probados | Demo de 6 capas, Qwen2.5 0,5B y 1,5B | Cualquiera convertido a Core ML | Miles en Hugging Face |
| Precisión | FP16 | FP16 en el ANE | Cuantizada, FP16 o BF16 |
Si quieres ejecutar un modelo de 8B o más en tu Mac, sigue con MLX o con modelos cuantizados en llama.cpp. Espresso tiene sentido si investigas el ANE, si te interesa el consumo por token de un modelo pequeño o si quieres ver cómo se programa un acelerador que Apple mantiene cerrado.
Preguntas frecuentes
¿Espresso funciona en un Mac con Intel?
No. Los Mac con Intel no tienen Neural Engine, y Espresso necesita Apple Silicon (M1 o posterior) con macOS 15 o superior.
¿Puedo usar Espresso en una app de la App Store?
No. El propio README avisa de que Apple rechaza las apps que usan _ANEClient o _ANEInMemoryModel. Para una app publicada, la vía es Core ML.
¿Es más rápido que MLX para chatear con un LLM?
El proyecto no lo afirma. Su única cifra de velocidad compara con Core ML en un modelo de 6 capas, y para Qwen2.5 solo publica paridad. El chat incluye un modo --vs mlx para que lo midas tú.
Conclusión
Espresso demuestra que el Neural Engine del Mac puede ejecutar un transformer desde Swift sin Core ML, y que fusionar capas lo hace 3,41 veces más rápido en un modelo pequeño. Con modelos reales el ANE solo se queda las proyecciones y la red feed-forward, y la memoria SRAM de unos 32 MB deja la capa de salida en la CPU. Si tienes un Mac con Apple Silicon, ejecuta la demo y el chat con --vs mlx para medirlo en tu equipo, pero no lo metas en una app que vaya a la App Store.
Fuentes: [1] Christopher Karani, repositorio Espresso[1], [2] Espresso, benchmarks/results/latest.json[5], [3] Espresso, informe de paridad de Qwen2.5-0.5B[6], [4] Espresso, informe de paridad de Qwen2.5-1.5B[7], [5] Apple Machine Learning Research, Deploying Transformers on the Apple Neural Engine[2], [6] Ramchand Kumaresan, Orion en arXiv[3], [7] mdaiter, repositorio ane[4].
Fuentes
Código fuente
Accede a todo el código fuente de este artículo en GitHub.
Ver en GitHub