Inicio / Artículos / Duplicar la velocidad de decodificación de Qwen3.8-Flash-Next con MTP en GPU RTX

Duplicar la velocidad de decodificación de Qwen3.8-Flash-Next con MTP en GPU RTX

Aprenda a compilar un fork de llama.cpp con decodificación especulativa MTP y los flags adecuados para duplicar más o menos la velocidad de generación de Qwen3.8-Flash-Next en equipos con varias RTX.

1248 palabras

Mucha gente dice que Qwen3.8-Flash-Next no está a la altura de sus resultados en benchmarks. Usado a diario junto a Qwen3.8-27B, se comporta de forma distinta a esa reputación. Para su tamaño no gasta muchos tokens de razonamiento, hace lo que se le pide sin discutir, mantiene el hilo de una sesión larga y recuerda bien el historial anterior. Comparado con DeepSeek V4 Flash 0731 es el asistente más agradable para el día a día, y es fácil acabar convirtiéndolo en el modelo por defecto.

La única queja es la velocidad. Con tres RTX 3090 y una RTX 3070 Ti, la decodificación se queda en unos 30 tokens por segundo. Es un goteo lento y constante, y alguien que escriba rápido va por delante.

Hay un remedio conocido y se llama MTP (multi-token prediction). Es la variante de DeepSeek de la decodificación especulativa: una cabeza de borrador ligera acoplada al modelo propone varios tokens siguientes y la red completa solo los verifica. Comprobar cuesta mucho menos que generar, así que pueden salir cinco tokens en el tiempo que normalmente lleva uno.

El problema es que llama.cpp no ha integrado MTP para este modelo. Tras mucho ensayo y error, una combinación de pesos, fork y flags duplicó más o menos el rendimiento con los mismos archivos del modelo, el mismo servidor y las mismas cuatro GPU. La receta está abajo y, después, la parte de por qué publicar esta función ha sido tan caótico.

Consiga los pesos adecuados

Para el modelo principal, descargue la versión Q4_K_M de 4,27bpw del repositorio GGUF de AtomicChat.

Esa cuantización se hizo pensando en los MacBook, ya que está dimensionada para la memoria unificada de Apple silicon. Aun así funciona bien en tarjetas RTX y necesita menos VRAM que la cuantización equivalente de Unsloth. A veces una versión empaquetada para un público acaba sirviendo a otro.

El repositorio también tiene una variante más pequeña, IQ4_XS de 3,84bpw. Se probaron las dos, y la pequeña es claramente peor. Ahorrar 0,4 bits por peso no compensa lo que se pierde.

La cabeza MTP viene como archivo sidecar aparte, que encontrará en la carpeta sidecar de Unsloth. Aquí se usó la cabeza Q8_0. Si va justo de VRAM, la cabeza Q4_K_M también funciona.

Compile el fork

El código que funciona está en la rama qwen4exp/mtp del fork de llama.cpp de danielhanchen. La propia documentación de Unsloth remite a esa misma rama, lo que dice mucho de dónde vive realmente esta función ahora mismo.

Es una compilación normal con CMake: instale los paquetes, clone la rama y configure.

apt-get update
apt-get install pciutils build-essential cmake curl libcurl4-openssl-dev -y
git clone --branch qwen4exp/mtp https://github.com/danielhanchen/llama.cpp
cmake llama.cpp -B llama.cpp/build \
    -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=ON
cmake --build llama.cpp/build --config Release -j --clean-first \
    --target llama-cli llama-mtmd-cli llama-server llama-gguf-split

Arranque el servidor

Este es un comando de arranque que funciona tal cual. Usa cuatro GPU, una ventana de contexto de 200K y un borrador MTP que puede proponer hasta 5 tokens por paso.

CUDA_VISIBLE_DEVICES=0,1,2,3 llama-server \
  -m .../Qwen3.8-Flash-Next-AD-4.27bpw-Q4_K_M-M64-00001-of-00033.gguf \
  --alias Qwen3.8-Flash-Next \
  --jinja \
  -ngl 99 \
  -c 200000 \
  --split-mode layer \
  --tensor-split 0.9,0.22,1.0,0.75 \
  -fit off \
  --host 0.0.0.0 --port 8082 \
  --metrics \
  --temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 --repeat-penalty 1.0 \
  --reasoning-format none \
  --reasoning-effort low \
  --parallel 1 \
  -md .../mtp-Qwen3.8-Flash-Next-Q8_0.gguf \
  --cache-type-k q8_0 --cache-type-v q8_0 \
  --spec-type draft-mtp --spec-draft-n-max 5

La mayor parte de la aceleración sale de unos pocos flags:

  • --spec-type draft-mtp activa la cabeza de borrador MTP. Hasta que upstream integre el trabajo, solo el fork conoce este flag.
  • --spec-draft-n-max 5 permite que el borrador adivine hasta cinco tokens por delante. Con este modelo la tasa de aceptación fue lo bastante alta como para que cinco merecieran la pena.
  • -md ... apunta al archivo sidecar que contiene la cabeza MTP, porque la cuantización principal no la incluye.
  • --parallel 1 no es opcional. Hay un bug abierto por el que MTP combinado con varios slots en paralelo hace que el contenido de una conversación se filtre en otra. Un único slot mantiene limpia la salida.
  • --split-mode layer con --tensor-split 0.9,0.22,1.0,0.75 reparte las capas entre cuatro tarjetas desiguales: tres 3090 de 24 GB y una 3070 Ti de 8 GB. Esas proporciones se ajustaron a esa combinación exacta, así que cuente con retocarlas para su hardware.
  • -fit off hace que llama.cpp cargue exactamente lo que usted indica en lugar de tomar sus propias decisiones de memoria.

Qué obtiene

En esa máquina, el antes y el después quedaron así:

Before MTP    After MTP
Decode speed          ~30 t/s       60–80 t/s
Model weights         same          same
Server hardware       same          same
Quality               same          same
Total VRAM used       76GB          ~80 GB

La diferencia se nota más de lo que se mide. A 30 tokens por segundo está viendo escribir a un modelo. A entre 60 y 80 está conversando con él. Los mismos pesos, la misma máquina, la misma mañana, y la calidad de la salida no se movió.

Cualquier equipo con más de 80 GB de VRAM puede ejecutar exactamente esta configuración y debería ver la misma mejora.

¿Por qué no se ha integrado todavía?

Porque el código abierto a veces sale mal, incluso cuando el código ya existe.

MTP para Qwen3.8-Flash-Next no es un problema de investigación sin resolver. vLLM y SGLang lo soportaron desde el primer día. Hay forks de llama.cpp que lo incluyen, y Unsloth lo integra en su aplicación de escritorio. Esos vídeos de demostración a 170 tokens por segundo son reales.

En el upstream de llama.cpp, cinco pull requests distintos intentaron incorporarlo y ninguno entró. Uno se quedó como borrador con conflictos de merge y sin una sola revisión. Otro se cerró después de que un colaborador enviara código ajeno sin el permiso de su autor. Un mantenedor explicó que dar crédito a alguien no es lo mismo que obtener su consentimiento, y desde entonces la mejor implementación funcional ha quedado huérfana. Un tercero, el más limpio de todos, lo cerró su propio autor la semana pasada con el mensaje de que de momento no se iba a impulsar en upstream. Puede leer usted mismo el PR estancado #27836 y el PR abandonado #28610.

Hay tres problemas difíciles alrededor de esos PR. El primero es una disputa sobre el formato GGUF: tres esquemas rivales sobre dónde se guarda la cabeza MTP. El segundo es el bug de corrección con los slots en paralelo, registrado en el issue #28286. El tercero son las regresiones en los backends, ya que la misma función que duplica la velocidad en CUDA supone una pérdida neta en Vulkan.

Nada de eso convierte a los mantenedores en los malos. Su regla es que la rama principal nunca debe romper el servidor de producción de nadie, y la decodificación especulativa en un modelo con estado híbrido es probablemente el diff más temible que se les puede enviar. Aun así, una función que funciona y una función que se publica son dos artefactos de ingeniería distintos, y la distancia entre ellos se mide en semanas de espera.

¿Merece la pena probarlo?

Una tarde de compilación hizo que un modelo de uso diario fuera el doble de rápido, y los comandos de arriba son la receta completa. Si tiene más de 80 GB de VRAM y un Qwen3.8-Flash-Next que suelta los tokens con cuentagotas, vale la pena el esfuerzo. La propia guía de MTP de Unsloth cubre la parte del proveedor.

Si la rama principal se pondrá al día es imposible saberlo. Los requisitos previos van llegando y el formato se está asentando, así que probablemente alguien llevará la implementación correcta a la rama correcta con la bendición de sus autores. Hasta entonces, la vanguardia vive en los forks, así que clone en consecuencia.