Qué es realmente whisper.cpp (y qué no es)
Cuando OpenAI publicó Whisper en septiembre de 2022, la implementación de referencia era un paquete Python construido sobre PyTorch. Funcionaba, pero arrastraba todo el árbol de dependencias de PyTorch (unos 2 GB), necesitaba CUDA para tener una velocidad razonable y era difícil de incluir dentro de una app de escritorio.
Georgi Gerganov, un ingeniero de software búlgaro con formación en simulaciones físicas, publicó whisper.cpp en octubre de 2022. El proyecto reescribe el bucle de inferencia de Whisper en C++ usando una biblioteca de tensores personalizada llamada GGML. El resultado es un binario autocontenido que no enlaza Python, PyTorch ni runtime de CUDA por defecto. Corre en una Raspberry Pi 4, un iPhone, un MacBook Air de 2015 o un Threadripper al máximo, y produce las mismas transcripciones que la referencia Python de OpenAI con un margen de error mínimo.
whisper.cpp no es un modelo diferente. La arquitectura de la red neuronal es idéntica: un encoder Transformer de 12 o 24 capas seguido de un decoder Transformer de 12 o 24 capas, según el tamaño. Lo que cambió es el formato de los pesos, el runtime que ejecuta las multiplicaciones matriciales y la ausencia de Python en el camino de despliegue.
El efecto práctico es que cualquier aplicación que quiera correr Whisper sin acceso a internet puede enlazar whisper.cpp estáticamente y distribuir un ejecutable autocontenido. SnailText hace esto. MacWhisper hace esto. SuperWhisper en modo local hace esto. Tres años después de que empezara el proyecto, whisper.cpp es lo bastante rápido para dictado en tiempo real en un teléfono.
GGML - la biblioteca de tensores que hace las matemáticas de verdad
GGML es una pequeña biblioteca C (unos 30.000 líneas a mediados de 2026) que gestiona cuatro cosas: un formato de archivo para pesos serializados de redes neuronales, una representación en grafo de la secuencia de operaciones que realiza una red, un backend de ejecución en CPU con kernels SIMD ajustados a mano, y un conjunto de backends GPU (Metal, CUDA, Vulkan, ROCm, SYCL) que delegan las multiplicaciones matriciales a aceleradores de hardware cuando hay uno disponible.
El formato de archivo - ahora llamado GGUF desde agosto de 2023, sustituyendo al formato GGML anterior - es un único archivo binario que contiene los metadatos de arquitectura del modelo, el vocabulario y los tensores de pesos cuantizados en una disposición diseñada para acceso mediante memory-mapped. Cuando whisper.cpp carga un modelo, no asigna gigabytes de RAM y copia los pesos. Llama a mmap (o el equivalente en Windows) y deja que el sistema operativo paginee los pesos a memoria a medida que el kernel los necesita. En un sistema con bastante RAM, el archivo termina cacheado en su totalidad. En un sistema ajustado, el SO desaloja las capas frías y mantiene las calientes en memoria. En cualquier caso, GGML no tiene que gestionarlo él mismo.
El modelo de ejecución es un grafo acíclico dirigido. Antes de la primera inferencia, GGML construye un nodo de grafo para cada operación tensorial que realiza Whisper: la auto-atención multi-cabeza, los MLP feed-forward, la cross-atención entre encoder y decoder, el softmax final. El grafo se compila una vez. En cada inferencia, GGML recorre el grafo, despachando cada nodo al backend que haya sido seleccionado. El despacho ocurre a la granularidad de una operación tensorial, no del grafo completo, lo que significa que un backend GPU que no soporte alguna operación exótica puede caer a CPU solo para ese nodo sin perder el resto de la aceleración GPU.
Cuantización INT8 e INT4 - cómo un modelo de 1,5 GB se convierte en 600 MB sin desmoronarse
El modelo Whisper Large, en su forma FP16 original de PyTorch, ocupa unos 3 GB en disco. Whisper Small ronda los 480 MB. Whisper Tiny ronda los 75 MB. Estas son las cifras FP16 de la publicación de OpenAI.
GGML almacena estos modelos en formatos cuantizados. El predeterminado para uso en producción es Q5_1, lo que significa que cada peso se codifica con unos 5,5 bits de media en lugar de 16. El resultado es una reducción de aproximadamente 3-4x en tamaño en disco y una reducción similar en uso de RAM. Whisper Large en Q5_1 cabe en 1,2 GB. Whisper Small cabe en unos 250 MB. Esto es lo que hace realista distribuir Whisper como parte de un instalador de aplicación de escritorio.
El truco está en cómo se asignan los bits. La cuantización ingenua - simplemente truncar cada peso FP16 a su valor INT8 más cercano - destruye la precisión. La arquitectura transformer es sensible a la magnitud de los pesos, y la cuantización uniforme produce suficiente error por capa que, multiplicado en 24 capas, el modelo simplemente produce nonsense.
GGML usa cuantización por bloques con factores de escala por bloque. Los pesos se dividen en bloques pequeños (típicamente 32 pesos por bloque), se calcula el valor absoluto máximo de cada bloque, y los pesos se escalan para caber en el presupuesto de bits disponible. Se almacena un pequeño factor de escala en coma flotante junto a cada bloque. En la decuantización, los valores enteros se multiplican por la escala para recuperar una aproximación del FP16 original. Para algunos formatos de cuantización, también se almacena un segundo valor de offset por bloque para manejar pesos con distribuciones asimétricas.
El formato Q5_1 usado por la mayoría de los despliegues de whisper.cpp en producción almacena enteros de 5 bits por peso, un zero-point de 4 bits por bloque y una escala FP16 por bloque. Los bits efectivos por peso incluyendo todo el overhead son unos 5,5. Como comparación, Q4_0 es alrededor de 4,5 bits por peso y Q8_0 es alrededor de 8,5 bits por peso.
El coste en precisión es medible pero pequeño. En el conjunto de prueba limpio de LibriSpeech, Whisper Small en FP16 obtiene alrededor de un 3,4% de tasa de error de palabras (según el paper original de Whisper, Tabla 2). El mismo modelo en Q5_1 obtiene alrededor de un 3,5-3,7% según la compilación. En Q4_0 la diferencia se amplía a unos 0,3-0,5 puntos porcentuales. Por debajo de Q4 la diferencia empieza a importar, y por debajo de Q3 se obtiene una degradación notable en habla con acento.
Los despliegues en producción (SnailText incluido) casi siempre distribuyen Q5_1 por defecto. Es el formato con el mejor equilibrio entre tamaño y precisión para las variantes compactas a medianas de Whisper. Los archivos completos del modelo Q5_1 para Whisper Tiny hasta Large-v3 están disponibles en Hugging Face bajo ggerganov/whisper.cpp.
Los tres backends GPU, y por qué existen todos
La inferencia en CPU funciona en cualquier máquina pero es el camino más lento. Una CPU moderna de portátil puede correr Whisper Small a aproximadamente 1,5-2x tiempo real, lo que significa que una grabación de 10 segundos tarda unos 5-7 segundos en transcribirse. Para dictado, donde el usuario espera ver el texto al cabo de un segundo de terminar la frase, eso es demasiado lento.
La aceleración GPU es el camino hacia la latencia inferior al segundo, y whisper.cpp admite tres backends GPU serios.
Metal es la API GPU de Apple. En cualquier Mac con Apple Silicon (M1 y posteriores), el chip M-series tiene una arquitectura de memoria unificada donde la GPU comparte la misma RAM que la CPU. Esto es inusual. En sistemas x86 con GPUs discretas, tienes que copiar datos de la RAM del sistema a la VRAM de la GPU antes de que la GPU pueda acceder a ellos, y copiar los resultados de vuelta cuando termina. En Apple Silicon no hay copia. La GPU y la CPU leen las mismas páginas de memoria física. El efecto práctico es que whisper.cpp con backend Metal en un M2 o M3 transcribe un clip de 10 segundos en unos 0,4-0,8 segundos para Whisper Small. M4 es aún más rápido. El backend Metal fue aportado por ingenieros de Apple y es el más pulido de los tres.
CUDA es la API GPU de NVIDIA. Es la plataforma de computación GPU original, que data de 2007, y sigue siendo el backend más rápido en throughput absoluto para modelos grandes en una tarjeta NVIDIA dedicada. Un RTX 4070 transcribe Whisper Large-v3 muy por debajo del tiempo real. La desventaja es que CUDA solo corre en hardware NVIDIA (sin AMD, sin Intel, sin gráficos integrados) y requiere que el usuario tenga instalado el CUDA Toolkit, que es una descarga de varios gigabytes y un punto de fricción para usuarios finales. whisper.cpp admite CUDA pero típicamente no es el backend predeterminado en aplicaciones dirigidas a consumidores.
Vulkan es la API GPU multiplataforma. Funciona en NVIDIA, AMD, Intel Arc, gráficos integrados AMD, gráficos integrados Intel y GPUs ARM Mali. El backend Vulkan en GGML y whisper.cpp llegó en 2024 y maduró rápidamente durante 2025-2026. En una tarjeta RTX discreta, Vulkan es aproximadamente un 70-90% de la velocidad de CUDA para la misma carga de trabajo. En hardware AMD e Intel, donde CUDA no es una opción, Vulkan es el único camino significativo hacia la aceleración. El backend Vulkan es lo que SnailText distribuye en Windows porque nos permite producir un único binario que acelera por GPU en cualquier portátil moderno sin pedirle al usuario que instale drivers.
También existen ROCm (el equivalente CUDA de AMD) y SYCL (el stack de computación de Intel). Ambos funcionan en whisper.cpp pero tienen bases de usuarios notablemente más pequeñas.
Cómo se traducen los números de producción en experiencia de usuario
La tabla de benchmarks anterior muestra los tiempos de extremo a extremo que medimos en configuraciones de hardware de consumo habituales. Vale la pena destacar tres observaciones.
Primero, los Macs con chips M alcanzan latencia interactiva (menos de un segundo para frases cortas de dictado) en todos los modelos excepto Large, incluso sin GPU discreta, gracias a la memoria unificada. Segundo, el backend Vulkan en una tarjeta NVIDIA moderna está dentro del 25-30% de CUDA, lo cual es suficientemente cercano para que la comodidad de no instalar CUDA gane para apps de consumo. Tercero, CPU-only en un portátil Intel reciente es lo bastante rápido para uso ocasional pero no lo bastante rápido para sentirse instantáneo.
Streaming - obteniendo transcripciones antes de que el usuario termine de hablar
Los números anteriores son latencia de extremo a extremo: pulsa la tecla de stop, espera el resultado. Hay un segundo modo, streaming, donde whisper.cpp transcribe el audio en trozos mientras el usuario sigue hablando. El resultado aparece a los pocos cientos de milisegundos de que el usuario pare, porque la mayor parte del trabajo ya está hecho.
El streaming requiere dos cosas. Un modelo de detección de actividad de voz (VAD) que identifique cuándo ha terminado una frase (la mayoría de los despliegues en producción usan Silero VAD, un pequeño modelo ONNX que corre en milisegundos). Y una forma de alimentar frases completadas a Whisper a medida que ocurren, sin reiniciar el modelo.
whisper.cpp admite ambas. El patrón estándar es: abre el stream de audio, corre VAD continuamente, acumula muestras hasta que VAD reporta que el usuario ha hecho una pausa, luego despacha ese trozo a Whisper mientras sigues grabando. Cada trozo se transcribe de forma independiente. Cuando el usuario pulsa la tecla de stop, solo el trozo parcial final tiene que transcribirse - los trozos anteriores ya están hechos.
SnailText usa este patrón donde el hardware se beneficia de él - para un dictado típico de 30 segundos en máquinas con GPU habilitada, el usuario ve el resultado al cabo de un segundo o dos de parar, porque la mayor parte de la inferencia ocurrió durante la propia grabación. Sin embargo, el streaming no es gratis: añade overhead que compensa cuando las pasadas de inferencia individuales son baratas, y compensa menos (o nada) cuando no lo son. Si activarlo para una configuración dada es el tipo de decisión que necesita telemetría de producción más que una regla general.
El problema de selección de GPU del que nadie te advierte
En un portátil con GPU Intel y NVIDIA, whisper.cpp puede caer silenciosamente a CPU aunque la GPU dedicada esté disponible. El backend Vulkan es maravilloso en teoría: un único binario, cualquier GPU, simplemente funciona. En la práctica hay una clase de problema que no está en ninguna documentación y que solo encuentras distribuyendo a hardware real de consumo.
En un portátil con múltiples GPUs - NVIDIA Optimus, AMD Switchable Graphics, integrada más dedicada - el sistema operativo las enumera en un orden. Las APIs gráficas las enumeran en órdenes diferentes, a veces en desacuerdo entre sí y a veces cambiando según el estado del driver. El backend Vulkan de whisper.cpp direcciona un dispositivo elegido por índice entero, y el entero que apunta a la tarjeta discreta en una API puede apuntar a la GPU integrada en otra.
El síntoma es una caída silenciosa a CPU. Crees que se está usando la GPU. La aplicación reporta que se está usando la GPU. El monitor del sistema muestra cero actividad GPU. La inferencia tarda 20 segundos en lugar de 2.
La clase de solución que se mantiene bajo este tipo de heterogeneidad es empírica más que predictiva: no intentes adivinar qué índice de dispositivo corresponde a una GPU real, mídelo. Ejecuta una inferencia de prueba rápida y decide basándote en el rendimiento observado si mantener ese dispositivo o caer al siguiente. El diagrama a continuación esboza la forma del bucle.
En máquinas de una sola GPU esto simplemente confirma lo obvio en una fracción de segundo al arrancar. En portátiles multi-GPU enruta la inferencia a la tarjeta discreta incluso cuando la enumeración del sistema es engañosa. Hay varios casos límite que te pillan en esta capa - entradas de enumeración duplicadas en algunas GPUs integradas, peculiaridades de driver específicas del fabricante, iGPUs de memoria compartida que necesitan presupuestos de latencia diferentes para evitar falsos negativos - y cada uno necesita su propio tratamiento. La lección general es que la selección de GPU en hardware Windows de consumo es un problema que solo puedes resolver observando cómo se comporta tu build en máquinas reales y añadiendo caminos para los casos que la telemetría saca a la luz.
Nada de esto está en la documentación de whisper.cpp ni en los issues del repositorio GGML. Es el tipo de trabajo que convierte “admitimos Vulkan” en algo sobre lo que un usuario no técnico no tendrá que pensar.
Categorías de fallos que solo ves en producción
La prueba y el fallback fue un ejemplo de una categoría más amplia — modos de fallo solo en producción que la documentación upstream no te avisa. Cada uno de estos nos requirió telemetría de usuarios reales para siquiera notarlo; los listamos aquí como categorías en lugar de recetas porque la solución correcta en cada caso depende de tu stack específico, y lo que funcionó para nosotros no es el único camino válido.
1. Desacuerdo en la enumeración multi-GPU. Cubierto arriba. La forma: diferentes APIs gráficas en la misma máquina no se ponen de acuerdo sobre qué índice entero apunta a qué GPU física. La clase de solución: sondeo empírico en lugar de selección predictiva. La versión de este problema que encuentras depende del SO, el estado del driver y si la máquina tiene gráficos híbridos — la clase de solución generaliza.
2. Enumeración fantasma de GPU integrada. En algunas configuraciones de GPU integrada, el loader Vulkan expone múltiples entradas que todas apuntan al mismo adaptador físico subyacente. Ejecutar inferencia contra cada una de ellas sucesivamente — que es lo que hace una prueba y fallback ingenua — puede saturar el ancho de banda de memoria compartida de formas que no son solo lentas sino activamente desestabilizadoras. La clase de solución: identificar la identidad del adaptador físico antes de iterar índices, para que un bucle de sondeo nunca golpee accidentalmente el mismo hardware múltiples veces.
3. Compilación en frío de shaders Vulkan. La inicialización GPU por primera vez en un estado de driver reciente puede tardar mucho más de lo que sugiere la inferencia en estado estable. Hemos visto tiempos de compilación de shaders en frío extenderse desde un segundo en hardware caliente hasta decenas de segundos en hardware frío. La clase de solución: hacer los timeouts adaptativos a si la GPU se ha usado recientemente, no codificarlos a un umbral único.
4. Presión de memoria GPU bajo streaming. La inferencia en streaming significa que múltiples invocaciones de Whisper ocurren en sucesión rápida en lugar de un único batch grande. En GPUs con VRAM limitada o compartida (gráficos integrados, chips NVIDIA móviles con TDR habilitado), las invocaciones consecutivas pueden desencadenar una recuperación a nivel de driver — el SO reinicia la GPU pensando que la app se ha colgado. La clase de solución: detectar las restricciones de VRAM por adelantado y deshabilitar el streaming o reducir la tasa de invocación en dispositivos que no están listos para ello.
5. Caída silenciosa a CPU. A lo largo de los cuatro puntos anteriores, el peor modo de fallo es que whisper.cpp no registre nada mal, la app reporte la GPU como activa, el monitor del sistema esté de acuerdo, y la inferencia tarde diez o veinte veces más de lo que debería. La clase de solución es la misma: no confíes en los metadatos sobre si estás en GPU — mide la latencia en una carga de trabajo real y decide basándote en la medición.
El hilo conductor es que la aceleración GPU en hardware de consumo es un problema que solo puedes resolver completamente desplegando a usuarios reales y observando la telemetría. Ninguna de estas categorías aparece en un entorno de desarrollo local con un stack gráfico bien entendido. Todas aparecieron en el momento en que tuvimos unos pocos miles de instalaciones con diferentes fabricantes, generaciones y versiones de driver.
Qué significa esto para alguien que construye sobre whisper.cpp hoy
Si estás evaluando whisper.cpp para un producto, la capacidad central es sólida. La precisión del modelo es buena, los formatos de cuantización están bien entendidos, y el proyecto está mantenido activamente por Gerganov y una comunidad open-source de unos 600 contribuidores a fecha de 2026.
La complejidad está en la larga cola. Conseguir que la aceleración GPU funcione de forma fiable en la diversidad del hardware de consumo es su propio proyecto de ingeniería. El comportamiento de streaming correcto depende del hardware que tienes delante. La ingeniería de prompts (el parámetro initial_prompt) afecta materialmente a la precisión en vocabulario específico del dominio, y tiene sus propias sutilezas según cómo lo uses. El comportamiento en grabaciones largas tiene casos límite que los valores predeterminados no gestionan bien. Los ajustes predeterminados son buenos pero no óptimos para todos los casos de uso.
Para una app de dictado de escritorio, todo eso es resoluble, y whisper.cpp sigue siendo la mejor base sobre la que resolverlo. Para un pipeline de transcripción en el lado del servidor a escala, podrías mirar faster-whisper (Python, backend CTranslate2) o una de las APIs en la nube - el perfil operacional es diferente. Para casos de uso embebidos (Raspberry Pi, móvil, dispositivos edge), whisper.cpp está en una categoría propia.
La razón por la que existe el proyecto, y la razón por la que SnailText, MacWhisper, SuperWhisper y muchos otros construyen sobre él, es que hace que Whisper sea genuinamente portable. El mismo código C++ corre en todos los SO de escritorio, todos los SO móviles modernos y una amplia gama de objetivos embebidos. El modelo en sí es solo un archivo. El runtime es una biblioteca estática. No hay Python, no hay CUDA, no hay servicio al que llamar. Eso, en 2026, sigue siendo una combinación poco habitual.
Si prefieres usar el resultado en vez de construirlo, es justo lo que distribuimos: SnailText ejecuta whisper.cpp en local en Mac y Windows, elige un modelo que encaja con tu hardware y se queda offline. Descárgalo aquí — gratis para empezar, sin cuenta.