Registro de optimización de LLMs en DGX Spark

Para una guía rápida de uso, véase Inicio. Este documento es el log detallado de trabajo.

Objetivo

Encontrar la mejor forma de ejecutar modelos locales optimizados para el NVIDIA DGX Spark (GB10 Grace Blackwell, ARM64/aarch64, 128 GB memoria unificada, CUDA 13.0, sm_121), enfocado en uso con agentes (Hermes/OpenClaw/n8n/Open WebUI).

Modelos objetivo:


1. Estado del sistema analizado

ComponenteValor
HardwareNVIDIA DGX Spark (GB10 Grace Blackwell)
CPU20 cores ARM64 (aarch64)
GPUNVIDIA GB10 (sm_121)
Memoria128 GB LPDDR5x unificada (~121 GB usable)
Driver NVIDIA580.142
CUDA13.0
Docker29.2.1
NVIDIA Container Toolkit1.19.0
Disco3.7 TB, 2.7 TB libres

Modelos/contenedores previos corriendo:


2. Lecciones clave sobre el DGX Spark

  1. ARM64/aarch64 es crítico: muchos contenedores x86_64 fallan con exec format error. Hay que verificar que las imágenes tengan manifest arm64.
  2. GB10 (sm_121) no tiene compute FP4 nativo: a diferencia de B200. Los pesos NVFP4 se ejecutan vía Marlin (--moe-backend marlin), que descomprime a BF16 en runtime. Esto limita la velocidad respecto a lo que se vería en B200.
  3. Ollama/llama.cpp no exprimen el Spark: usan backends genéricos sin kernels específicos de Blackwell ni NVFP4 nativo.
  4. Para agentes, el modelo importa más que el framework: Gemma 4 31B dense está limitado por ancho de banda (~6-7 tok/s en GB10). Gemma 4 26B-A4B MoE (~3.8B activos) es el candidato a 50 tok/s.

3. Resultados de pruebas

3.1 Gemma 4 26B-A4B NVFP4 con vLLM

Modelos probados:

Contenedores que funcionan:

ConfiguraciónDecode tok/sTTFT calienteNotas
Oficial base (gemma4-0505-cu130)~30.1~0.20sEstable, tool calling activado
Community + parche (gemma4-cu130)~49.5~0.08sMejor opción para agentes
Community, gpu_util 0.92, max-seqs 4, batched 8192~49.3~1.9sSin mejora real
Community, gpu_util 0.90, batched 2048~49.3~2.4sSin mejora real
n-gram speculative decoding~24.6~2.5sEmpeoró (prompt no repetitivo)
MTP (google/gemma-4-26B-A4B-it-assistant)Error-AssertionError en shape del drafter con esta build

Comando recomendado (community + parche):

docker run -d --name gemma4-26b-a4b \
  --gpus all --ipc host --network host --shm-size 64gb \
  -v ~/vllm/gemma4-26b-a4b-nvfp4-community:/models/gemma4 \
  -v ~/vllm/gemma4-26b-a4b-nvfp4-community/gemma4_patched.py:/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/gemma4.py \
  -e HF_HOME=/models \
  vllm/vllm-openai:gemma4-cu130 \
    --model /models/gemma4 \
    --served-model-name gemma-4-26b-a4b \
    --host 0.0.0.0 --port 8000 \
    --quantization modelopt \
    --moe-backend marlin \
    --trust-remote-code \
    --kv-cache-dtype fp8 \
    --max-model-len 32768 \
    --max-num-seqs 2 \
    --max-num-batched-tokens 4096 \
    --gpu-memory-utilization 0.90 \
    --enable-prefix-caching \
    --enable-auto-tool-choice \
    --tool-call-parser pythonic

Problemas encontrados:

3.2 Gemma 4 31B IT (dense)

Modelo probado: nvidia/Gemma-4-31B-IT-NVFP4 (~31 GB) Contenedor: vllm/vllm-openai:gemma4-0505-cu130

ConfiguraciónDecode tok/sTTFT calienteNotas
Base NVFP4~6.7~1.8sLimitado por ancho de banda de memoria

Nota: no es candidato a 50 tok/s por ser denso y limitado por ancho de banda de memoria.

3.3 Qwen 3.6 35B-A3B

Modelos probados:

Contenedores:

ConfiguraciónCheckpointContenedorDecode tok/sTTFT calienteNotas
NVIDIA NVFP4 W4A16 + marlin + flashinfernvidia/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:nightly~75–77~0.10sRecomendación actual. modelopt W4A16, parser qwen3_coder, fastsafetensors, async-scheduling, 262K contexto.
Base (compressed-tensors + marlin)RedHatAI/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:gemma4-0505-cu130~42.2~0.10sFallback estable, tool calling activado
max-seqs 4, batched 8192, gpu_util 0.92RedHatAI/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:gemma4-0505-cu130~42.2~1.4sSin mejora real
n-gram speculative (num_spec_tokens=5)RedHatAI/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:gemma4-0505-cu130~34–37~0.10sEmpeora para texto no repetitivo
MTP (qwen3_5_mtp)RedHatAI/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:gemma4-0505-cu130Error-Drafter no cuantizado no soporta moe_backend='marlin'
TRT-LLM 1.3.0rc13 (MLP-only NVFP4 propio)Cuantizado desde BF16nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13~34.4~0.09sCuantizado con Model Optimizer 0.44.0 desde BF16. Ver sección 4.

Comando recomendado:

./scripts/run-qwen36-35b-a3b.sh

El script usa el checkpoint nvidia/Qwen3.6-35B-A3B-NVFP4, vLLM nightly, 262K de contexto, flashinfer, async-scheduling, fastsafetensors y el parser qwen3_coder para tool calling.

3.4 NVIDIA Nemotron 3

Modelos probados:

TensorRT-LLM 1.3.0rc13

Contenedor: nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13

ConfiguraciónDecode tok/sTTFT calienteMemoria picoNotas
Nano-30B-A3B BF16~28.8~0.22 s~118 GBCarga directa; casi llena el pool unificado.
Super-120B-A12B NVFP4~14.7~0.29 s~110 GBCheckpoint NVFP4 oficial; más lento por más expertos activos.

vLLM gemma4-0505-cu130

Contenedor: vllm/vllm-openai:gemma4-0505-cu130

ConfiguraciónDecode tok/sTTFT calienteMemoria picoNotas
Nano-30B-A3B BF16~28.3~0.20 s~72 GBFunciona; requiere detener otros servicios grandes.
Super-120B-A12B NVFP4———No viable: CUDA OOM al inicializar engine; colgó el Spark en el primer intento.

Comando recomendado (Nano):

docker run -d --name nemotron3-nano-30b-a3b-trtllm \
  --gpus all --ipc host --network host --shm-size 64gb \
  -v ~/vllm/nemotron3-nano-30b-a3b-bf16:/models/nemotron \
  -e HF_HOME=/models \
  nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13 \
    trtllm-serve /models/nemotron --host 0.0.0.0 --port 8000 \
    --backend pytorch --max_seq_len 8192 --max_batch_size 1 --kv_cache_dtype fp8

Comando recomendado (Super):

docker run -d --name nemotron3-super-120b-a12b-trtllm \
  --gpus all --ipc host --network host --shm-size 64gb \
  -v ~/vllm/nemotron3-super-120b-a12b-nvfp4:/models/nemotron \
  -e HF_HOME=/models \
  nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13 \
    trtllm-serve /models/nemotron --host 0.0.0.0 --port 8000 \
    --backend pytorch --max_seq_len 8192 --max_batch_size 1 --kv_cache_dtype fp8

Comando alternativo (Nano con vLLM):

docker run -d --name nemotron3-nano-30b-a3b-vllm \
  --gpus all --ipc host --network host --shm-size 64gb \
  -v ~/vllm/nemotron3-nano-30b-a3b-bf16:/models/nemotron \
  -e HF_HOME=/models \
  vllm/vllm-openai:gemma4-0505-cu130 \
    --model /models/nemotron --served-model-name nemotron3-nano-30b-a3b \
    --host 0.0.0.0 --port 8000 --kv-cache-dtype fp8 \
    --max-model-len 8192 --max-num-seqs 1 --max-num-batched-tokens 4096 \
    --gpu-memory-utilization 0.90 --trust-remote-code

Intento fallido: Nemotron-3 Super 120B-A12B con vLLM

Se intentó servir nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 con vllm/vllm-openai:gemma4-0505-cu130.

Primer intento: el contenedor cargó los 17 shards (~75 GB en disco) en ~489 s. Los últimos logs antes del colgado:

Loading weights took 489.67 seconds
WARNING ... Your GPU does not have native support for FP4 computation ...
Using MoEPrepareAndFinalizeNoDPEPModular
Using uncalibrated q_scale 1.0 ...

El Spark se colgó. Causa raíz: dos servidores llama-server (Qwen3.6-27B-Q8_0 y Qwen3.6-35B-A3B-Q8_0) ocupaban ~76 GB de VRAM, dejando insuficiente memoria para vLLM.

Segundo intento (tras reinicio y liberar memoria): vLLM falló inmediatamente con:

torch.AcceleratorError: CUDA error: out of memory
  File ".../vllm/utils/mem_utils.py", line 108, in measure
    self.free_memory, self.total_memory = current_platform.mem_get_info(device)

Incluso con ~117 GB libres en el host, vLLM no pudo inicializar el EngineCore.

Diagnóstico: el modelo NemotronHForCausalLM tiene 88 capas, 512 expertos enrutados, 22 expertos por token, 1 experto compartido, hidden size 4096. El overhead de descompresión FP4 vía Marlin, la reserva de KV cache por --gpu-memory-utilization 0.90 y la compilación de kernels del V1 engine exceden el margen de 128 GB unificados. TensorRT-LLM, en cambio, usa un presupuesto de memoria fijo (--max_seq_len 8192 --max_batch_size 1) que resulta estable.

Conclusión: Nemotron-3 Super 120B-A12B solo es viable con TensorRT-LLM en GB10.

3.5 NVIDIA Nemotron 3 Nano Omni (multimodal)

Modelo probado:

Contenedor: vllm/vllm-openai:gemma4-0505-cu130

ConfiguraciónDecode tok/sTTFT calienteMemoria picoMultimodalNotas
vLLM, --quantization modelopt_fp4, --moe-backend marlin~40.0~0.10 s~40 GBImagen ✅Texto rápido; imagen responde correctamente.
Audio (vía OpenAI input_audio)–––Audio ❌Falló con Invalid or unsupported audio file en la decodificación del contenedor.

TRT-LLM 1.3.0rc13 también fue probado, pero falló al parsear mensajes multimodales:

AttributeError: 'NoneType' object has no attribute 'model_type' en parse_chat_messages_coroutines.

Comando recomendado:

docker run -d --name nemotron3-nano-omni-vllm \
  --gpus all --ipc host --network host --shm-size 64gb \
  -v ~/vllm/nemotron3-nano-omni-30b-a3b-reasoning-nvfp4:/models/nemotron \
  -e HF_HOME=/models \
  vllm/vllm-openai:gemma4-0505-cu130 \
    --model /models/nemotron \
    --served-model-name nemotron3-nano-omni \
    --host 0.0.0.0 --port 8000 \
    --quantization modelopt_fp4 \
    --moe-backend marlin \
    --kv-cache-dtype fp8 \
    --max-model-len 8192 \
    --max-num-seqs 1 \
    --max-num-batched-tokens 4096 \
    --gpu-memory-utilization 0.90 \
    --trust-remote-code

Prueba de imagen:

python3 benchmarks/test_multimodal.py nemotron3-nano-omni image /ruta/a/imagen.png

4. Intento con TensorRT-LLM oficial de NVIDIA

Objetivo: validar si los modelos oficiales de NVIDIA corren mejor con los contenedores TensorRT-LLM diseñados para DGX Spark.

Contenedores probados

ContenedorTRT-LLMGemma 4 26B-A4B oficialQwen 3.6 35B-A3B oficialNemotron 3 NanoNemotron 3 Super
nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev1.1.0rc3No soporta gemma4Error de opción/quantização––
nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc101.3.0rc10No soporta gemma4AssertionError quant_algo––
nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc131.3.0rc13No soporta gemma4AssertionError quant_algo (pre-cuantizado), OK MLP-only propioOK BF16OK NVFP4

Errores clave

Cuantización propia con TensorRT Model Optimizer

Se intentó el flujo oficial para Qwen 3.6 desde el modelo base BF16 (Qwen/Qwen3.6-35B-A3B) usando nvidia-modelopt 0.44.0 (los ejemplos de 0.35.0 no son compatibles con PyTorch 2.12 disponible en el sistema).

Pasos ejecutados:

  1. Descargar Qwen/Qwen3.6-35B-A3B BF16 (~70 GB).
  2. Convertir el checkpoint VLM a text-only (qwen3_5_moe_text) porque Model Optimizer no carga Qwen3_5MoeForConditionalGeneration directamente.
  3. Parchear modelopt.torch.quantization.plugins.huggingface._QuantFusedExperts.iter_weights_for_calibration para soportar quantizers por experto de Qwen3.5/3.6.
  4. Parchear example_utils.py para usar la memoria total de la GPU en lugar de la libre reportada por accelerate (necesario en memoria unificada del GB10).
  5. Cuantizar con --qformat nvfp4 y --qformat nvfp4_mlp_only.

Resultados:

Quant configTamañoError/success al servir con TRT-LLM 1.3.0rc13
Full NVFP4~20 GBNotImplementedError: split linear-attention packing no soporta input_scale/weight_scale de atención lineal cuantizada.
MLP-only NVFP4~22 GBSirve correctamente con trtllm-serve --backend pytorch.

Benchmark TRT-LLM MLP-only:

Conclusión del intento

Los blueprints oficiales de Spark funcionan con modelos como nvidia/Llama-3.1-8B-Instruct-FP4, openai/gpt-oss-* y nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16, pero no out-of-the-box con nvidia/Gemma-4-26B-A4B-NVFP4 ni nvidia/Qwen3.6-35B-A3B-NVFP4.

Para TensorRT-LLM con Qwen 3.6 hay un camino funcional hoy:

El formato full NVFP4 aún no es compatible con TRT-LLM 1.3.0rc13 por la atención lineal de Qwen3.5/3.6. Para Gemma 4, seguir esperando soporte nativo de gemma4 en TRT-LLM.

vLLM sigue siendo la opción más rápida y sencilla para los checkpoints pre-cuantizados; TRT-LLM es viable para Qwen 3.6 con cuantización MLP-only manual.


5. Configuraciones finales recomendadas

ModeloCheckpointContenedorDecode tok/sUso recomendado
Qwen 3.6 35B-A3Bnvidia/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:nightly~75–77Mejor calidad-velocidad; contexto máximo 262K; tool calling robusto.
Gemma 4 26B-A4Bbg-digitalservices/Gemma-4-26B-A4B-it-NVFP4 + parchevllm/vllm-openai:gemma4-cu130~49.5Máxima velocidad para agentes
Qwen 3.6 35B-A3BRedHatAI/Qwen3.6-35B-A3B-NVFP4vllm/vllm-openai:gemma4-0505-cu130~42.2Fallback estable
Gemma 4 31Bnvidia/Gemma-4-31B-IT-NVFP4vllm/vllm-openai:gemma4-0505-cu130~6.7Solo si se necesita el denso
Qwen 3.6 35B-A3BCuantiizado propio MLP-only NVFP4nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13~34.4Alternativa TRT-LLM; usa stack oficial
Nemotron-3-Nano-30B-A3Bnvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13~28.8Modelo denso BF16 de NVIDIA; usa casi toda la memoria
Nemotron-3-Nano-30B-A3Bnvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16vllm/vllm-openai:gemma4-0505-cu130~28.3Alternativa vLLM; requiere liberar VRAM de otros servicios
Nemotron-3-Super-120B-A12Bnvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc13~14.7Modelo grande NVFP4; prioriza calidad sobre velocidad. No usar con vLLM
Nemotron-3-Nano-Omni-30B-A3Bnvidia/NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4vllm/vllm-openai:gemma4-0505-cu130~40.0Mejor opción multimodal: texto + imagen funcionan; audio pendiente

6. Notas técnicas


7. Archivos entregables

8. Próximos pasos opcionales

  1. Validar MTP speculative decoding con el checkpoint nvidia + vLLM nightly (en pruebas anteriores con RedHatAI empeoró el decode single-user).
  2. Probar --tool-call-parser gemma4 en Gemma 4 para tool calling nativo.
  3. Evaluar calidad de los modelos en tareas de agentes (Hermes/OpenClaw).
  4. Probar full NVFP4 de Qwen 3.6 en TRT-LLM cuando se soporten los scales de atención lineal.
  5. Comparar calidad de Qwen 3.6 nvidia W4A16 vs RedHatAI compressed-tensors vs MLP-only NVFP4 propio.
  6. Probar GPT-OSS con TRT-LLM.