Ir al contenido principal

Cómo acelerar LLM locales con DFlash y decodificación especulativa

Aprende a acelerar la inferencia local de Gemma 4 31B en una única RTX 4090 usando decodificación especulativa DFlash y Flash Attention frente a una configuración de referencia.
Actualizado 17 jun 2026

En las últimas semanas he visto mucho movimiento en la comunidad de r/LocalLLaMA en torno a la decodificación especulativa, DFlash, una mejor gestión de la caché KV y compilaciones optimizadas de llama.cpp. Lo interesante es que la gente está logrando grandes mejoras de velocidad sin cambiar de hardware.

En esta guía, ejecutaremos Gemma 4 31B IT de forma local en una RTX 4090 24GB usando BeeLlama.cpp, un fork de llama.cpp que admite decodificación especulativa DFlash.

Probaremos el modelo de dos formas. Primero lo ejecutaremos sin DFlash para tener una línea base. Después lo ejecutaremos con DFlash y compararemos la mejora de velocidad.

Obtén una certificación superior en IA

Demuestra que puedes utilizar la IA de forma eficaz y responsable.
Certifícate, que te contraten

¿Qué es DFlash?

En pocas palabras, DFlash usa un modelo borrador para predecir varios tokens por adelantado, mientras que el modelo principal verifica esos tokens en lugar de generarlos uno a uno. Cuando se aceptan muchos tokens del borrador, la generación se acelera mucho manteniendo una salida similar a la del modelo original.

En mi experimento, DFlash logró casi un aumento de velocidad de 3,7× en ciertas tareas, con resultados muy similares a la línea base. El objetivo de esta guía es mostrar la configuración, ejecutar ambas versiones y comparar resultados con claridad.

Cómo funciona DFlash

La generación estándar en LLM es lenta porque la mayoría de modelos generan texto token a token. Cada token depende del anterior, así que el modelo avanza paso a paso por la respuesta.

DFlash acelera estos up using speculative decoding.

En lugar de pedirle al modelo principal que genere cada token directamente, DFlash emplea un modelo borrador independiente que adivina varios tokens próximos primero. Después, el modelo principal verifica esos tokens del borrador en un paso mayor. Si los tokens del borrador son buenos, el modelo principal los acepta; si alguno es incorrecto, lo corrige y continúa.

Una forma sencilla de entenderlo:

  • Sin DFlash: el modelo principal escribe un token cada vez.
  • Con DFlash: el modelo borrador propone un bloque de tokens y el modelo principal comprueba rápidamente cuáles puede aceptar.

Diagram of the DFlash speculative decoding workflow.

Diagrama del flujo de trabajo de la decodificación especulativa con DFlash.

Esto es especialmente útil en tareas estructuradas como la programación. El código suele seguir patrones predecibles: imports, definiciones de funciones, sangrías, bucles y sintaxis común. Por eso, el modelo borrador acierta a menudo los siguientes tokens, permitiendo que el modelo principal acepte más tokens en cada paso.

DFlash vs MTP: ¿en qué se diferencian?

DFlash y Multi-Token Prediction (MTP) persiguen el mismo objetivo: ayudar al modelo a generar más de un token por cada costoso paso de decodificación.

La diferencia está en cómo crean los tokens del borrador.

Método

Cómo funciona

¿Modelo extra?

Punto fuerte

MTP

Usa cabezales integrados de predicción multi-token para anticipar tokens futuros

Normalmente no necesita modelo borrador aparte

Configuración más simple cuando el modelo ya admite MTP

DFlash

Usa un modelo borrador DFlash independiente para proponer bloques más grandes de tokens

Puede lograr grandes mejoras en salidas estructuradas como código

En términos simples, MTP suele venir integrado en el propio modelo. Predice varios tokens futuros con cabezales internos, así que cuando está soportado puede ser más sencillo de configurar y más eficiente en memoria.

DFlash, en cambio, usa un modelo borrador aparte. Esto hace la configuración algo más pesada, pero permite un drafting más agresivo. Por eso DFlash puede ofrecer grandes mejoras en tareas estructuradas donde es más fácil predecir los siguientes tokens.

1. Preparar el entorno

Te recomiendo ejecutar esta configuración en local si tienes una GPU RTX 3090 o RTX 4090. Si no, puedes alquilar una GPU en RunPod, Vast.ai u otro proveedor.

Para esta guía usaremos un pod de RunPod con RTX 4090. Partí de la última plantilla de RunPod para PyTorch y apliqué unos cambios:

  • Exponer el puerto 8910 para el servidor de llama.cpp
  • Aumentar el almacenamiento persistente a 100 GB
  • Añadir mi token de Hugging Face para acelerar la descarga de modelos

Editing the latest Pytorch template in the Runpod

Con esta configuración, el pod cuesta alrededor de 0,70 $ por hora, según el precio y la disponibilidad actuales en RunPod.

Runpod Pytorch docker running on the RTC 4090 machine.

Cuando el pod esté desplegado, abre JupyterLab desde el panel de RunPod. Lanza un terminal nuevo e instala las dependencias básicas:

apt update
apt install -y git cmake build-essential curl wget python3-pip

Installing Linux pages within the Jupyter Terminal.

2. Clonar BeeLlama.cpp

Ahora vamos a clonar BeeLlama.cpp, el fork de llama.cpp que usaremos en esta configuración.

BeeLlama.cpp está diseñado para inferencia local más rápida en GGUF manteniendo el flujo de trabajo conocido de llama.cpp. Conservas las mismas herramientas, incluido llama-server, pero con funciones extra centradas en el rendimiento como decodificación especulativa DFlash, control adaptativo del borrador y compresión de caché KV TurboQuant/TCQ

Ejecuta estos comandos dentro de tu terminal de JupyterLab terminal:

git clone https://github.com/Anbeeld/beellama.cpp.git
cd beellama.cpp

Esto descargará el repositorio de BeeLlama.cpp y te llevará a la carpeta del proyecto. Todos los comandos de compilación del siguiente paso deben ejecutarse dentro de este directorio.

3. Compilar BeeLlama.cpp con CUDA

Ahora compilaremos BeeLlama.cpp con soporte CUDA para aprovechar bien la RTX 4090.

En esta configuración activaremos CUDA, Flash Attention, optimizaciones nativas de CPU y kernels de Flash Attention cuantizados. Como usamos una RTX 4090, fijamos además la arquitectura CUDA a 89.

cmake -B build -DGGML_CUDA=ON -DGGML_NATIVE=ON \
 -DGGML_CUDA_FA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON \
 -DCMAKE_CUDA_ARCHITECTURES=89 \
 -DCMAKE_BUILD_TYPE=Release

cmake --build build -j

La compilación puede tardar 20 minutos. Durante el proceso, quizá veas avisos relacionados con TurboQuant, TCQ o declaraciones CUDA de DFlash. En mi caso fueron solo advertencias y no detuvieron la compilación.

BeeLlama.cpp with CUDA build is complete

Por último, copia el binario del servidor en la carpeta principal del proyecto para ejecutarlo más fácilmente: 

cp ./build/bin/llama-server ./llama-server

4. Instalar la CLI de Hugging Face y descargar los modelos

Ahora necesitamos descargar dos archivos GGUF: el modelo principal y el modelo borrador de DFlash.

El modelo principal es el que produce la salida final. El modelo borrador de DFlash es mucho más pequeño y solo se usa para predecir tokens por delante del principal. El modelo principal sigue verificando los tokens generados; el borrador está para acelerar la decodificación, no para sustituir al principal.

Primero, instala la CLI de Hugging Face:

pip install -U huggingface_hub

Luego crea una carpeta para mantener los archivos del modelo organizados:

mkdir -p models

Descarga el modelo principal Gemma 4 31B IT en formato GGUF:

hf download unsloth/gemma-4-31B-it-GGUF \
gemma-4-31B-it-Q4_K_S.gguf \
--local-dir models

A continuación, descarga el modelo borrador de DFlash:

hf download Anbeeld/gemma-4-31B-it-DFlash-GGUF \
gemma4-31b-it-dflash-Q5_K_M.gguf \
--local-dir models

El modelo borrador de DFlash aparece en Hugging Face como un modelo con arquitectura dflash-draft; el archivo Q5_K_M pesa sobre 1,09 GB, por lo que es mucho más pequeño que el modelo principal de 31B. Esto hace viable cargarlo junto al principal para la decodificación especulativa. 

5. Ejecutar Gemma 4 31B sin DFlash

Antes de activar DFlash, primero ejecutaremos Gemma 4 31B de forma normal. Esto nos da una referencia de velocidad de generación, uso de VRAM y calidad de salida. Después compararemos esta base con la ejecución con DFlash para ver la ganancia real.

Ejecuta este comando desde dentro de la carpeta beellama.cpp:

./llama-server \
 -m "models/gemma-4-31B-it-Q4_K_S.gguf"  \
--host 0.0.0.0  \
 --port 8910 \
-np 1 \
-ngl all \
-b 2048 -ub 512 \
--ctx-size 32768  \
--cache-type-k q5_0 \
--cache-type-v q4_1 \
--flash-attn on \
--jinja \
--metrics \
--log-timestamps \
--log-prefix \
--reasoning off \
--temp 0.7 \
--top-k 64 \
--top-p 0.95 \
--min-p 0.0

Este comando inicia el servidor del modelo en el puerto 8910. Como expusimos el puerto 8910 al crear el pod en RunPod, podemos acceder al modelo directamente desde el navegador.

Una vez que el modelo se haya cargado en la memoria de la GPU, verás un mensaje indicando que el servidor está ejecutándose en: 0.0.0.0:8910.

The Gemma 4 31B Without DFlash inference serve is running locally.

Vuelve ahora al panel de RunPod y haz clic en el enlace del puerto asociado al 8910. 

Access the 8910 port from the Runpod Dashboard

Se abrirá la interfaz web de llama.cpp, donde podrás probar el modelo en una sencilla UI de chat. 

Testing the Gemma 4 31B Without DFlash

En este punto, lanza algunas preguntas largas o complejas para observar la velocidad media de tokens. En mi ejecución base sin DFlash, obtenía de media unos 41 tokens por segundo. 

Testing the Gemma 4 31B Without DFlash

6. Evaluar el modelo de referencia

Con el modelo base en marcha, necesitamos una forma sencilla de medir su velocidad de generación. Para ello usaremos tres prompts de programación y los enviaremos al servidor local de llama.cpp mediante el endpoint de chat completions compatible con OpenAI.

El objetivo no es crear una batería de benchmarks perfecta. Solo queremos una referencia consistente para comparar luego los mismos prompts con DFlash activado.

Abre una nueva pestaña de Terminal en Jupyter y crea un script de prueba:

cat > test_llm_prompts.sh <<'EOF'
#!/usr/bin/env bash

PORT="${1:-8910}"
MODEL="${2:-local-gemma}"
PREFIX="${3:-run}"

URL="http://localhost:${PORT}/v1/chat/completions"

PROMPTS=(
"Write a complete Python task store module. Include a Task dataclass, TaskStatus enum, TaskStore class, add_task, update_task, delete_task, search_tasks, filter_by_status, export_to_json, get_all_tasks, and 5 tests. Return only one complete Python file."

"Write a complete Python key-value report module. Include a KeyValueStore class, set, get, delete, exists, list_keys, filter_by_prefix, export_to_json, load_from_json, and a generate_report function that returns total keys, empty values, prefix counts, and largest value length. Include 5 tests. Return only one complete Python file."

"Write a complete Python doubly linked list module. Include a Node dataclass, DoublyLinkedList class, append, prepend, delete, find, reverse, to_list, from_list, clear, and 5 tests. Return only one complete Python file."
)

echo "Testing server: $URL"
echo "Model: $MODEL"
echo "Output prefix: $PREFIX"

for i in "${!PROMPTS[@]}"; do
  NUM=$((i+1))
  OUT="${PREFIX}_prompt_${NUM}.json"

  echo ""
  echo "Running prompt ${NUM}..."
  echo "Saving to ${OUT}"
  echo "--------------------------------"

  jq -n \
    --arg model "$MODEL" \
    --arg prompt "${PROMPTS[$i]}" \
    '{
      model: $model,
      messages: [
        {
          role: "user",
          content: $prompt
        }
      ],
      max_tokens: 1200,
      temperature: 0.7
    }' | curl -s "$URL" \
      -H "Content-Type: application/json" \
      -d @- | tee "$OUT" | jq '.timings'

  echo "Saved full result to ${OUT}"
done

echo ""
echo "Summary"
echo "--------------------------------"

for f in ${PREFIX}_prompt_*.json; do
  echo "$f"
  jq '{
    model: .model,
    prompt_tokens: .usage.prompt_tokens,
    completion_tokens: .usage.completion_tokens,
    total_tokens: .usage.total_tokens,
    generation_speed_tok_s: .timings.predicted_per_second,
    generation_time_sec: (.timings.predicted_ms / 1000),
    draft_tokens: .timings.draft_n,
    accepted_draft_tokens: .timings.draft_n_accepted
  }' "$f"
done
EOF

En macOS o Linux, recuerda dar permisos de ejecución al script:

chmod +x test_llm_prompts.sh

Después ejecútalo contra el modelo base: 

./test_llm_prompts.sh 8910 local-gemma-baseline baseline

Este script envía tres prompts de generación de código en Python al modelo y guarda cada respuesta completa como un archivo JSON. También imprime métricas útiles: tokens completados, velocidad y tiempo de generación, y campos relacionados con los tokens del borrador. 

La salida completa es larga, así que aquí va un breve resumen de los resultados base. Nos da una visión rápida del rendimiento antes de activar DFlash.

Prompt

Tokens de la completion

Velocidad de generación

Tiempo de generación

Prompt 1: Módulo de task store

1124

40,66 tok/s

27,64 s

Prompt 2: Módulo de informes key-value

1200

40,67 tok/s

29,51 s

Prompt 3: Módulo de lista doblemente enlazada

1200

40,72 tok/s

29,47 s

En los tres prompts, el modelo base se mantuvo muy constante alrededor de 40,68 tokens por segundo. Esta será nuestra referencia antes de probar con DFlash.

7. Ejecutar Gemma 4 31B con DFlash

Con la referencia lista, ejecutaremos el mismo modelo pero con DFlash activado.

Vuelve al terminal donde corre el servidor base y detenlo con Ctrl + C

Luego inicia el servidor optimizado con DFlash:

./llama-server \
-m "models/gemma-4-31B-it-Q4_K_S.gguf" \
--spec-draft-model "models/gemma4-31b-it-dflash-Q5_K_M.gguf" \
--spec-type dflash \
--spec-dflash-cross-ctx 1024 \
--host 0.0.0.0  \
 --port 8910 \
-np 1 \
--kv-unified \
-ngl all \
--spec-draft-ngl all \
-b 2048 -ub 512 \
--ctx-size 32768 \
--flash-attn on \
--cache-ram 0 \
--jinja \
--no-mmap \
--mlock \
--no-host \
--metrics \
--log-timestamps \
--log-prefix \
--reasoning off \
--temp 0.7 \
--top-k 64 \
--top-p 0.95 \
--min-p 0.0

Este comando carga el mismo modelo principal Gemma 4 31B, pero ahora también carga el modelo borrador de DFlash mediante --spec-draft-model.

Las banderas clave relacionadas con DFlash son:

Flag

Propósito

--spec-draft-model

Carga el modelo borrador DFlash

--spec-type dflash

Activa la decodificación especulativa DFlash

--spec-dflash-cross-ctx 1024

Define la ventana cross-context usada por DFlash

--spec-draft-ngl all

Descarga las capas del modelo borrador a la GPU

--kv-unified

Usa gestión unificada de la KV para el principal y el borrador

Puede tardar un poco más en arrancar porque hay que cargar ambos modelos en memoria.

Loading the Gemma 4 31B With DFlash in the GPU memory

Cuando el servidor esté cargado, de nuevo verás el servicio de inferencia en: 0.0.0.0:8910.

Running the Gemma 4 31B With DFlash locally

8. Evaluar el modelo con DFlash

Vuelve al terminal de Jupyter donde creamos el script de benchmark. Podemos ejecutar el mismo script de nuevo, esta vez contra el servidor con DFlash activado. 

./test_llm_prompts.sh 8910 local-gemma-dflash dflash

Se usan los mismos tres prompts de código que en la prueba base, lo que hace que la comparación sea justa. La única diferencia importante es que ahora el servidor corre con el modelo borrador de DFlash.

Comparar la velocidad de inferencia

La salida completa es larga, así que aquí tienes un resumen rápido de los resultados base y con DFlashs:

Prompt

Velocidad base

Velocidad DFlash

Mejora

Tiempo base

Tiempo DFlash

Tiempo ahorrado

Módulo task store

40,66 tok/s

130,96 tok/s

3,22×

27,64 s

8,23 s

19,41 s

Módulo de informes key-value

40,67 tok/s

145,68 tok/s

3,58×

29,51 s

8,24 s

21,27 s

Módulo de lista doblemente enlazada

40,72 tok/s

153,04 tok/s

3,76×

29,47 s

7,84 s

21,63 s

En estas tres tareas de código, DFlash elevó la velocidad de generación de unos 40 tok/s a 130–153 tok/s. Es decir, aproximadamente un 3,2× a 3,8×, y el tiempo por prompt bajó de casi 30 segundos a unos 8 segundos

También puedes abrir el mismo enlace del puerto 8910 desde el panel de RunPod y probar el modelo en la interfaz web.

Comparar la calidad de salida

Como nos acercamos a un 4× en velocidad con prompts de código, lo siguiente es comprobar la calidad. Para ello probé el modelo en varias tareas distintas.

Primero le pedí que generara una web de portfolio sencilla para «Abid». Para ser un modelo local de 31B en una sola RTX 4090, el resultado fue muy bueno: estructura limpia con HTML y estilos utilizables. 

Website generated by Gemma 4

Después le pedí un diagrama de una canalización MLOps completa. El modelo devolvió código Mermaid con etiquetas, colores y un flujo completo. Probé el código y funcionó a la primera. 

Testing the Gemma 4 31B With DFlash for the diagram generation.

Luego le pedí que escribiera un blog sobre on Mixture of Experts en LLMs. La calidad seguía siendo alta, pero la velocidad bajó a unos 95 tok/s. Sigue siendo mucho más rápido que la base, pero más lento que en tareas de código. 

Testing the Gemma 4 31B With DFlash for blog writing.

Tiene sentido: DFlash brilla cuando la salida es más predecible. Las tareas de programación siguen patrones claros, así que el borrador acierta más tokens. En redacción creativa o prompts de investigación hay menos previsibilidad, se aceptan menos tokens del borrador y la ganancia puede ser menor. 

Reflexiones finales

Tras probar esta configuración, creo que la decodificación especulativa combinada con una mejor gestión de la caché KV es la gran ganadora para la inferencia local de LLM.

Lo mejor no es solo el incremento de velocidad sobre el papel, sino lo que te permite hacer. Cuando un modelo de 31B puede generar código a 130–150 tokens por segundo en una sola RTX 4090, empieza a ser práctico como asistente de codificación local. Puedes usarlo para crear proyectos desde cero, conectarlo con servidores MCP, ejecutar herramientas bash, usar habilidades personalizadas y montar un flujo de trabajo muy cercano al de agentes de código premium.

Para quienes ya tienen una RTX 3090 o 4090, esto es aún más atractivo. En lugar de pagar por cada asistente de código o depender totalmente de la nube, puedes ejecutar una configuración local potente, rápida, privada y flexible. Quizá no sustituya todas las herramientas alojadas para todo el mundo, pero para entusiastas de la IA local, desarrolladores y makers, está muy cerca.

Y esto no ha hecho más que empezar. Mucha gente ya está probando configuraciones similares con modelos nuevos como Qwen3.6-27B y reportan incluso mejor calidad. A medida que mejoren los modelos, los borradores y motores de inferencia como BeeLlama.cpp se optimicen, la IA local será cada vez más útil.

Lo mejor es la comunidad. Muchas de estas mejoras vienen de entusiastas de la IA local que experimentan, miden, mejoran las herramientas y comparten sus resultados abiertamente. Eso nos facilita al resto replicar la configuración y disfrutar de las mismas ganancias de rendimiento.

Temas
Relacionado

Tutorial

Cómo ajustar GPT 3.5: Liberar todo el potencial de la IA

Explore GPT-3.5 Turbo y descubra el potencial transformador del ajuste fino. Aprenda a personalizar este modelo de lenguaje avanzado para aplicaciones especializadas, mejore su rendimiento y comprenda los costes asociados, la seguridad y las consideraciones de privacidad.
Moez Ali's photo

Moez Ali

Tutorial

Guía para principiantes de LlaMA-Factory WebUI: Ajuste de los LLM

Aprende a afinar los LLM en conjuntos de datos personalizados, evaluar el rendimiento y exportar y servir modelos sin problemas utilizando el marco de trabajo de bajo/ningún código de LLaMA-Factory.
Abid Ali Awan's photo

Abid Ali Awan

Tutorial

Guía introductoria para el ajuste preciso de los LLM

El ajuste preciso de los grandes modelos lingüísticos (LLM) ha revolucionado el procesamiento del lenguaje natural (PLN) y ofrece capacidades sin precedentes en tareas como la traducción lingüística, el análisis del sentimiento y la generación de textos. Este enfoque transformador aprovecha modelos preentrenados como el GPT-2 y mejora su rendimiento en dominios específicos mediante el proceso de ajuste preciso.
Josep Ferrer's photo

Josep Ferrer

Tutorial

Tutorial FLAN-T5: Guía y puesta a punto

Una guía completa para afinar un modelo FLAN-T5 para una tarea de respuesta a preguntas utilizando la biblioteca de transformadores, y ejecutando la inferencia optmizada en un escenario del mundo real.
Zoumana Keita 's photo

Zoumana Keita

Tutorial

Cómo formar a un LLM con PyTorch

Domine el proceso de entrenamiento de grandes modelos lingüísticos con PyTorch, desde la configuración inicial hasta la implementación final.
Zoumana Keita 's photo

Zoumana Keita

Tutorial

Tutorial de DeepSeek-Coder-V2: Ejemplos, instalación, puntos de referencia

DeepSeek-Coder-V2 es un modelo de lenguaje de código de código abierto que rivaliza con el rendimiento de GPT-4, Gemini 1.5 Pro, Claude 3 Opus, Llama 3 70B o Codestral.
Dimitri Didmanidze's photo

Dimitri Didmanidze

Ver másVer más