Metodología de benchmark
asiai sigue estándares de benchmarking establecidos (MLPerf, SPEC CPU 2017, NVIDIA GenAI-Perf) para producir resultados fiables, reproducibles y comparables.
Protocolo
- Verificación previa: Rechaza iniciar si la presión de memoria es crítica o el sistema está severamente limitado (<80%)
- Calentamiento: 1 generación no cronometrada por motor para preparar compiladores JIT y cachés
- Ejecuciones medidas: Por defecto 3 ejecuciones por prompt por motor (configurable con
--runs) - Muestreo:
temperature=0(greedy) para salida determinista - Descarga de modelo: Después del benchmark de cada motor, el modelo se descarga para liberar memoria unificada antes de iniciar el siguiente motor. Esto previene la acumulación de memoria y el swapping al comparar múltiples motores con modelos grandes
- Enfriamiento adaptativo: Tras la descarga, asiai espera a que la presión de memoria de macOS vuelva a "normal" (máx. 30s), luego añade un mínimo de 5s de enfriamiento térmico
- Verificaciones de cordura: Los resultados con tok/s ≤ 0 se descartan. TTFT > 60s o tok/s > 500 generan advertencias (probable swapping o errores de medición)
- Reporte: Mediana de tok/s como métrica principal (estándar SPEC), media ± desviación estándar como secundaria
- Throttling: Advertencia emitida si
thermal_speed_limit < 100%durante cualquier ejecución. La degradación térmica (disminución monótona de tok/s entre ejecuciones, caída ≥ 5%) se detecta e informa - Metadatos: Versión del motor, formato del modelo, cuantización, chip de hardware, versión de macOS almacenados por resultado
Métricas
tok/s — Velocidad de generación
Tokens por segundo de tiempo de generación únicamente, excluyendo el procesamiento del prompt (TTFT).
Ollama (API nativa, /api/generate):
tok_per_sec = eval_count / (eval_duration_ns / 1e9)
Motores compatibles con OpenAI (LM Studio, llama.cpp, mlx-lm, vllm-mlx):
generation_s = wall_clock_s - ttft_s
tok_per_sec = completion_tokens / generation_s
Conteo de tokens: desde usage.completion_tokens en la respuesta del servidor. Si el servidor no reporta este campo, asiai recurre a len(text) // 4 y registra una advertencia. Este respaldo puede desviarse ~25%.
Validación cruzada (abril 2026, Qwen3.5-35B NVFP4, M4 Pro 64GB):
| Método | tok/s | Delta vs referencia |
|---|---|---|
| Ollama nativo (GPU interno) | 66.6 | referencia |
| OpenAI streaming (cliente) | 66.1 | -0.8% |
Con tamaños de contexto grandes (ej. 64k tokens), el TTFT puede dominar la duración total. Excluirlo de tok/s evita que generadores rápidos parezcan lentos.
TTFT — Tiempo al primer token
Tiempo entre el envío de la solicitud y la recepción del primer token de salida, en milisegundos.
Desde v1.6.0, asiai mide dos valores de TTFT para Ollama, y uno para todos los demás motores:
Ollama (medición dual):
- TTFT del lado del servidor (
ttft_ms): extraído deprompt_eval_durationen la respuesta de Ollama. Es el tiempo puro de procesamiento del prompt en GPU sin sobrecarga de red — la medición más precisa posible. Reportado comottft_source: server. - TTFT del lado del cliente (
ttft_client_ms): medido a la llegada del primer fragmento SSE con contenido. Incluye configuración HTTP, transmisión de solicitud y procesamiento del servidor. Es el mismo método utilizado para todos los demás motores.
Motores compatibles con OpenAI (LM Studio, llama.cpp, mlx-lm, vllm-mlx):
- TTFT del lado del cliente (
ttft_client_ms): medido en el primer fragmento SSE con contenido. Es la única medición disponible ya que estos motores no exponen el timing interno de procesamiento del prompt. Tantottft_mscomottft_client_mscontienen el mismo valor.
Métrica comparable: ttft_client_ms es la métrica comparable entre motores — utiliza el mismo método de medición independientemente del motor. Úsela para comparar TTFT entre diferentes motores. El ttft_ms del lado del servidor de Ollama es más preciso para el tiempo absoluto de procesamiento del prompt, pero no es directamente comparable con otros motores.
Validación cruzada (abril 2026, Qwen3.5-35B NVFP4, M4 Pro 64GB):
| Método | TTFT | Delta |
|---|---|---|
Ollama del lado del servidor (ttft_ms) |
27 ms | referencia |
Ollama del lado del cliente (ttft_client_ms) |
51 ms | +24 ms |
El delta de 24ms representa la sobrecarga HTTP en localhost. Esta sobrecarga es consistente y predecible, pero lo suficientemente significativa como para importar al comparar motores.
Power — Vatios de GPU
Potencia promedio de GPU durante la ejecución, medida mediante el framework Apple IOReport Energy Model (sin necesidad de sudo). Una medición por motor — sin promedios a nivel de sesión.
tok/s/W — Eficiencia energética
tok_per_sec_per_watt = tok_per_sec / power_watts
Varianza — Desviación estándar combinada
Desviación estándar combinada intra-prompt que captura el ruido entre ejecuciones sin mezclar la varianza entre prompts. Utiliza la corrección de Bessel (denominador N-1) para varianza muestral insesgada.
Clasificación de estabilidad:
- CV < 5% →
stable - CV < 10% →
variable - CV >= 10% →
unstable
Donde CV = (std_dev / mean) * 100.
VRAM — Uso de memoria
Primario: API nativa del motor (Ollama /api/ps, LM Studio /v1/models).
Respaldo: ri_phys_footprint vía ctypes (igual que el Monitor de Actividad). Marcado como "(est.)" en la interfaz.
Seguridad del entorno
asiai realiza verificaciones previas al benchmark:
- Presión de memoria: rechaza iniciar si es crítica
- Throttling térmico: advierte si el límite de velocidad < 80%
- Procesos duplicados: advierte si hay múltiples instancias del mismo motor ejecutándose (ej. dos procesos
ollama serveen el mismo puerto) - Tipo de runner del motor: para Ollama, detecta si el runner
--mlx-engineo--ollama-engineestá activo
Estas verificaciones previenen errores de medición causados por contención de recursos o enrutamiento incorrecto.
Conformidad
| Práctica | Estado |
|---|---|
| Verificación previa (presión de memoria + térmica) | Implementado |
| Detección de procesos duplicados | Implementado (v1.5.0) |
| Detección de tipo de runner Ollama (MLX vs llama.cpp) | Implementado (v1.5.0) |
| TTFT separado de tok/s | Implementado |
| Etiquetado de fuente TTFT (server vs client) | Implementado (v1.5.0) |
| Medición dual de TTFT (server + client) | Implementado (v1.6.0) |
| Muestreo determinista (temperature=0) | Implementado |
| Conteo de tokens desde API del servidor (no fragmentos SSE) | Implementado (advertencia en respaldo) |
| Monitoreo de energía por motor (IOReport, sin sudo) | Implementado |
| 1 generación de calentamiento por motor | Implementado |
| 3 ejecuciones por defecto (mínimo SPEC) | Implementado |
| Mediana como métrica principal (estándar SPEC) | Implementado |
| Desviación estándar combinada intra-prompt (Bessel N-1) | Implementado (corregido v1.5.0) |
| Descarga de modelo entre motores | Implementado |
| Enfriamiento adaptativo (sensible a presión de memoria) | Implementado |
| Verificaciones de cordura (tok/s, límites TTFT) | Implementado |
| Detección de throttling térmico + advertencia | Implementado |
| Detección de degradación térmica (disminución monótona) | Implementado |
| Versión del motor + tipo de runner almacenados por resultado | Implementado (v1.5.0) |
| VRAM universal vía ri_phys_footprint | Implementado |
| Detección de regresión histórica | Implementado |
| Script de validación cruzada (3 métodos comparados) | Disponible (scripts/cross-validate-bench.py) |
Consideraciones sobre Apple Silicon
Memoria unificada
Apple Silicon comparte memoria entre CPU y GPU. asiai ejecuta los motores secuencialmente y descarga los modelos entre motores para evitar contención de memoria y swapping. La VRAM es reportada nativamente por Ollama y LM Studio; para otros motores, asiai estima el uso de memoria mediante ri_phys_footprint (la métrica de huella física de macOS, igual que el Monitor de Actividad). Los valores estimados se etiquetan como "(est.)" en la interfaz.
Throttling térmico
- MacBook Air (sin ventilador): throttling severo bajo carga sostenida
- MacBook Pro (con ventilador): throttling leve
- Mac Mini/Studio/Pro: refrigeración activa, throttling mínimo
asiai registra thermal_speed_limit por resultado y advierte si se detecta throttling.
Caché KV
Tamaños de contexto grandes (32k+) pueden causar inestabilidad en motores que pre-asignan la caché KV. Configura la longitud de contexto del motor para que coincida con el tamaño real de la prueba para obtener resultados justos.
Medición de energía
asiai mide el consumo de energía de GPU, CPU, ANE y DRAM mediante el framework Apple IOReport Energy Model — sin necesidad de sudo. La energía se mide automáticamente en cada benchmark y cada snapshot de monitoreo.
IOReport lee los mismos contadores de energía del hardware que sudo powermetrics, pero a través de una API de espacio de usuario (libIOReport.dylib vía ctypes). Esto elimina la necesidad de configuración de sudo sin contraseña.
Validación
Validamos cruzadamente IOReport contra sudo powermetrics bajo carga de inferencia LLM en M4 Pro 64GB, usando 10 muestras pareadas por motor a intervalos de 2 segundos:
| Motor | Promedio IOReport | Promedio powermetrics | Delta medio | Delta máximo |
|---|---|---|---|---|
| LM Studio (MLX) | 12.6 W | 12.6 W | 0.9% | 2.1% |
| Ollama (llama.cpp) | 15.6 W | 15.4 W | 1.3% | 4.1% |
Ambos motores confirmaron un delta promedio <1,5% con 10/10 muestras pareadas. La energía de ANE fue 0.000W en las 20 muestras, confirmando que ningún motor LLM utiliza actualmente el Neural Engine.
La opción --power habilita validación cruzada adicional ejecutando simultáneamente IOReport y sudo powermetrics, almacenando ambas lecturas para comparación.
Eficiencia energética
La eficiencia energética (tok/s por vatio) se calcula como tok_per_sec / gpu_watts para cada resultado de benchmark. Esta métrica permite comparar el costo de inferencia entre motores y hardware.
Metadatos
Cada resultado de benchmark almacena: engine, engine_version, model, model_format, model_quantization, hw_chip, os_version, thermal_level, thermal_speed_limit, power_watts, power_source, metrics_version. Esto permite una comparación de regresión justa y benchmarks entre máquinas.