Saltar al contenido principal

Implementar TensorRT-Model-Connect en Jetson AGX Orin

Implementar TensorRT-Model-Connect en Jetson AGX Orin

Este wiki muestra cómo ejecutar NVIDIA TensorRT-Model-Connect (TRTMC) en un reComputer de Seeed impulsado por Jetson AGX Orin. Una vez que se compila el runtime nativo, el camino es corto: se parte de un checkpoint de Hugging Face, se produce un .bundle de TensorRT en el dispositivo y se ejecuta la generación de texto. No hay un host x86 separado ni un paso de exportación a ONNX.

Este recorrido utiliza Qwen3-4B-Instruct-2507 en FP16. Ese modelo es un transformer denso, por lo que TensorRT puede usar un motor de prefill por lotes. En esta misma página también se registran la memoria de conversión en AGX Orin 64GB, además de las dos cargas de trabajo en las que TRTMC es más rápido que llama.cpp CUDA: prefill largo en Qwen3-4B y decodificación codiciosa en Qwen3-0.6B.

nota

TensorRT-Model-Connect es una vista previa pública. Las APIs, la cobertura de modelos y el flujo de compilación aún pueden cambiar. La propia recomendación de NVIDIA es usar TRTMC cuando quieras probar modelos compatibles rápidamente y empezar con TensorRT Edge-LLM cuando necesites un runtime de LLM/VLM orientado a producción en Jetson.

¿Qué es TensorRT-Model-Connect?​

TensorRT-Model-Connect es un conjunto de implementaciones de referencia por familia de modelos sobre NVIDIA TensorRT. Un generador específico de la familia lee un snapshot de Hugging Face, construye motores de TensorRT y escribe un .bundle versionado. El mismo bundle se puede ejecutar desde la CLI trtmc o desde APIs nativas de tareas en C++, como generación de texto.

La diferencia práctica en Jetson es la ruta de conversión. TensorRT Edge-LLM sigue esperando que cuantices y exportes ONNX en una máquina Linux x86 con una GPU discreta y luego copies esos archivos ONNX al dispositivo para la generación de motores. TRTMC construye el bundle de motores en el propio Jetson.

flowchart LR
subgraph EdgeLLM["TensorRT Edge-LLM"]
A1["Hugging Face checkpoint"] --> A2["Quantize + ONNX export on x86 GPU"]
A2 --> A3["Copy ONNX to Jetson"]
A3 --> A4["Build engine on Jetson"]
A4 --> A5["llm_inference"]
end
subgraph TRTMC["TensorRT-Model-Connect"]
B1["Hugging Face checkpoint"] --> B2["python -m tensorrt_model_connect build"]
B2 --> B3[".bundle on Jetson"]
B3 --> B4["trtmc run"]
end
Conversión en dispositivo con TensorRT-Model-Connect comparada con una ruta de exportación ONNX en x86

Por qué esta ruta es más sencilla en Jetson​

TemaTensorRT Edge-LLMTensorRT-Model-Connect
Dónde se ejecuta la conversiónExportación ONNX en un host Linux x86 con una GPU NVIDIA y luego compilación del motor en JetsonSnapshot de Hugging Face a .bundle en Jetson
Archivos intermediosGrafos ONNX, a menudo de decenas de gigabytes, copiados al dispositivoNinguno. El .bundle es el artefacto
Comandos después de la configuraciónExportar, copiar, compilar el motor y luego ejecutar llm_inferencepython -m tensorrt_model_connect build y trtmc run
Memoria del host durante la conversiónDocumentación del proveedor: la exportación ONNX en FP8 puede necesitar alrededor de 18–20× el tamaño del modelo en RAM de CPU en la máquina x86Medido en AGX Orin 64GB para Qwen3-4B FP16: alrededor de 30,6 GB usados en el host, 41 GB de pico en el contenedor
Mejor encajeDespliegue de LLM/VLM en producción en JetsonProbar rápidamente un modelo compatible en la misma máquina que lo ejecutará

La fila de memoria no es una comparación con el mismo modelo. Las cifras publicadas de Edge-LLM para la exportación ONNX explican por qué ese flujo de trabajo suele salir de Jetson y ocupar una estación de trabajo con mucha RAM. Las cifras de TRTMC que aparecen a continuación son lo que realmente observamos al compilar Qwen3-4B FP16 en AGX Orin 64GB.

Hardware​

Este tutorial se reprodujo en reComputer Classic J5012 con Jetson AGX Orin 64GB. La conversión de Qwen3-4B FP16 alcanzó un pico de alrededor de 41 GB dentro del contenedor, por lo que 64 GB de memoria unificada es el objetivo práctico. J5011 (32GB) pertenece a la misma familia de productos, pero es probable que esta conversión haga thrashing o falle allí.

reComputer Classic J5012 con Jetson AGX Orin 64GB
reComputer Classic J5012
NVIDIA Jetson AGX Orin 64GB · verificado en este wiki
reComputer Classic J5011 con Jetson AGX Orin 32GB
reComputer Classic J5011
NVIDIA Jetson AGX Orin 32GB · misma familia de placas

Requisitos previos​

Prepara lo siguiente en el Jetson:

  • reComputer Classic J5012 u otro sistema Jetson AGX Orin 64GB
  • JetPack 7.2 / Ubuntu 24.04 / L4T R39.2
  • Docker con el runtime de NVIDIA (docker info debería listar nvidia en Runtimes)
  • Alrededor de 40 GB de disco libre para la imagen de desarrollo, el snapshot de Hugging Face y el .bundle
  • Acceso de red a GitHub, Hugging Face y nvcr.io
  • Opcional para la sección de benchmarks: nvpmodel MAXN y jetson_clocks

La pila de software verificada fue:

ElementoVersión
DispositivoJetson AGX Orin 64GB
SOUbuntu 24.04.4 LTS, kernel 6.8.12-tegra
L4TR39.2
CUDA en la imagen de TRTMC13.3.1
TensorRT en la imagen de TRTMC11.1.0.106 (TensorRT 26.07)
SM de la GPU87 (Orin)
Modo de energíaMAXN, GPU 1300 MHz
tip

Si docker ps devuelve permission denied, añade tu usuario al grupo docker y vuelve a iniciar sesión:

sudo usermod -aG docker $USER

1. Clonar TensorRT-Model-Connect​

git clone https://github.com/NVIDIA/TensorRT-Model-Connect.git
cd TensorRT-Model-Connect

La ruta oficial de inicio rápido es Build from Source más Quick Start. Los comandos siguientes siguen esa ruta, con las banderas específicas de Jetson ya rellenadas.

2. Compilar el contenedor de desarrollo​

En AGX Orin la capacidad de cómputo es 8.7, así que TRTMC_SM=87. Las imágenes de Docker para Jetson también necesitan --runtime nvidia.

GPU=0
SM="$(
nvidia-smi -i "$GPU" \
--query-gpu=compute_cap \
--format=csv,noheader,nounits |
tr -d '.[:space:]'
)"
IMAGE="trtmc-quickstart"

docker build \
-f Dockerfile.dev.aarch64 \
-t "$IMAGE" \
requirements

SOURCE_DIR="$(git rev-parse --show-toplevel)"

docker run --rm -it \
--runtime nvidia \
--gpus "device=${GPU}" \
--ipc=host \
--network host \
--mount "type=bind,source=${SOURCE_DIR},target=/src" \
--workdir /src \
--env TRTMC_SM="$SM" \
"$IMAGE" \
bash
precaución

Mantén los comandos restantes dentro de este contenedor. La imagen ya fija TensorRT 11.1, un venv de Python 3.12 y los headers nativos usados por la compilación de la familia Qwen. Si nvidia-smi no está disponible en tu Jetson, establece SM=87 manualmente.

Si el snapshot de Hugging Face y el bundle van a residir en un SSD externo, haz también un bind-mount de ese disco, por ejemplo -v /path/to/data:/out.

3. Compilar el runtime nativo​

Ejecuta estos comandos en el contenedor:

python -m pip install --no-deps -e . -C py-only=true

TRTMC_BUILD_DIR="build-sm${TRTMC_SM}"

cmake -S . -B "$TRTMC_BUILD_DIR" -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CUDA_ARCHITECTURES="${TRTMC_SM}-real" \
-DTRTMC_BUILD_BACKEND_RTX=OFF \
-DTRTMC_BUILD_TESTS=OFF \
-DTRTMC_BUILD_EXAMPLES=OFF

cmake --build "$TRTMC_BUILD_DIR" --parallel "$(nproc)" --target \
trtmc \
trtmc_backend_trt \
trtmc_model_qwen

export PATH="$PWD/$TRTMC_BUILD_DIR:$PATH"

trtmc busca bibliotecas nativas desde --runtime-root. Apunta ese argumento al directorio de compilación de CMake, que debe contener libtrtmc_core.so, libtrtmc_runtime.so, libtrtmc_backend_trt.so y libtrtmc_model_qwen.so.

4. Compilar el paquete Qwen3-4B en Jetson​

Este es el paso de conversión que antes requería una máquina x86 con GPU. Fija --max-sequence-length. La configuración de Hugging Face para este checkpoint anuncia un contexto de 262.144 tokens, y dejar ese valor predeterminado agotará la memoria durante la generación del motor.

python -m tensorrt_model_connect build Qwen/Qwen3-4B-Instruct-2507 \
--precision fp16 \
--backend trt \
--max-sequence-length 2048 \
--max-batch-size 1 \
--tensor-parallel-size 1 \
--output qwen3-4b-instruct-2507.bundle \
--verbose

Un directorio de snapshot local puede reemplazar el ID del modelo. El generador descarga el checkpoint, deja que la familia Qwen lo reclame y escribe un único .bundle que ya contiene los motores de TensorRT.

En la unidad J5012 verificada, Qwen3-4B FP16 con max_sequence_length=2048 produjo la siguiente huella de conversión. La ruta pública de Qwen de un solo dispositivo en FP16 escribe motores divididos (engine.plan + prefill.plan); las cifras siguientes se tomaron de una compilación de doble perfil, donde prefill y decode comparten un plan con dos perfiles de optimización.

MétricaValor medido
Precisión / disposiciónFP16, doble perfil
Longitud máxima de secuencia2048
Generación del motor128.1 s
Pesos de TensorRT7.49 GiB
Memoria de activación de prefill633 MiB
Memoria de activación de decode12 MiB
Pico de GPU del asignador de TensorRT7,674 MiB
Uso de host, pico30,640 MB
Memoria del contenedor, pico41.09 GiB
Python RSS, pico24.7 GB
.bundle final7.6 GiB
tip

Esos picos de host/contenedor son la razón por la que este wiki recomienda AGX Orin 64GB. Estás convirtiendo en el dispositivo perimetral, pero aún necesitas suficiente memoria unificada para que TensorRT construya los motores.

Inspecciona el paquete antes de ejecutarlo:

trtmc inspect ./qwen3-4b-instruct-2507.bundle

trtmc inspect imprime JSON. Busca family: qwen, task: text_generation y una lista de secciones que incluya engine.plan más los archivos del tokenizador. Una compilación dividida en FP16 también lista prefill.plan. Todavía no hay un grafo ONNX en el paquete.

5. Ejecutar inferencia​

trtmc run ./qwen3-4b-instruct-2507.bundle \
--runtime-root "$PWD/build-sm${TRTMC_SM}" \
--prompt "What is the capital of France? Answer in one word." \
--use-chat-template true \
--enable-thinking false \
--max-new-tokens 32

Una ejecución correcta imprime una respuesta corta como Paris. El mismo paquete se puede cargar desde C++ con trtmc::load_task(bundle, runtime_root) y la interfaz ITextGeneration; consulta la API de tareas en C++.

Rendimiento de inferencia frente a llama.cpp​

La comodidad de la conversión es solo la mitad de la historia. En el mismo J5012 comparamos TRTMC FP16 con llama.cpp CUDA + flash-attention FP16, ambos con contexto 2048. El gráfico y la tabla siguientes conservan solo las cargas de trabajo donde TRTMC es más rápido.

TensorRT-Model-Connect vs llama.cpp on Jetson AGX Orin 64GB: Qwen3-4B prefill and Qwen3-0.6B decode
Carga de trabajoTensorRT-Model-Connectllama.cpp CUDAVentaja de TRTMC
Qwen3-4B-Instruct-2507 prefill largo1,967 tok/s · 606.9 ms / 1,194 tok1,601 tok/s · 743.2 ms / 1,190 tok+22.9%
Qwen3-0.6B decode, 256 tokens nuevos69.4 tok/s65.5 tok/s+6.0%
Qwen3-0.6B decode después de un prompt de 1,212 tokens69.6 tok/s63.0 tok/s+10.5%

El resultado de prefill 4B es la mediana de tres ejecuciones medidas después de un calentamiento. Los resultados de decode 0.6B son de una ejecución medida después del calentamiento, con el modelo mantenido cargado durante toda la petición para que la caché KV se llene una vez y luego se generen muchos tokens.

nota

Usa un transformador Qwen3 denso para esta comparación. Los modelos híbridos Mamba como NVIDIA Nano-9B actualmente hacen prefill token por token en TRTMC, por lo que son una mala forma de juzgar el rendimiento de TensorRT.

Reproducir las cargas de trabajo más rápidas​

Bloquea primero las frecuencias, luego mantén el modelo cargado durante toda la petición. Informa las líneas de temporización del motor, no el tiempo de pared del proceso. El tiempo de pared del proceso incluye la deserialización de TensorRT / carga de GGUF y ocultará la diferencia.

sudo nvpmodel -m 0   # MAXN; confirm with nvpmodel -q
sudo jetson_clocks

llama.cpp en esta placa se compiló con CUDA y flash-attention, y luego se ejecutó con todas las capas en la GPU:

cmake -S llama.cpp -B llama.cpp/build-cuda \
-DGGML_CUDA=ON -DGGML_CUDA_FA=ON -DGGML_NATIVE=ON \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_CUDA_ARCHITECTURES=87-real
cmake --build llama.cpp/build-cuda -j

1. Qwen3-4B prefill largo​

Esta es la carga de trabajo donde ayuda un motor de prefill de TensorRT por lotes. Compila el paquete FP16 de la sección 4 con --max-sequence-length 2048, convierte el mismo snapshot a GGUF F16 y luego alimenta a ambos runtimes con el mismo prompt largo.

Configuración compartida para esta fila:

  • Modelo: Qwen/Qwen3-4B-Instruct-2507, FP16, contexto 2048
  • Prompt: unos 6,000 caracteres de contexto más una pregunta sobre París de 70 palabras (1,190–1,194 tokens después de la plantilla de chat)
  • Decode: 64 tokens nuevos, codicioso (temperature=0, top-k=1)
  • TRTMC: paquete de doble perfil, plantilla de chat activada, thinking desactivado
  • llama.cpp: GGML_CUDA=ON, GGML_CUDA_FA=ON, -ngl 99 -c 2048 -b 512 -ub 512 -fa on --jinja
  • Calentamiento 1 + 3 medidos; usa la mediana

Convierte el mismo snapshot de Hugging Face a GGUF F16 para llama.cpp:

python3 convert_hf_to_gguf.py Qwen3-4B-Instruct-2507 \
--outfile Qwen3-4B-Instruct-2507-F16.gguf \
--outtype f16

Escribe el prompt largo y luego ejecuta TRTMC. Lee [trtmc-perf] Prefill (o la temporización del motor qwen prefill).

python3 - <<'PY'
from pathlib import Path
filler = (
"Transit planners track on-time performance, bus bunching, depot energy use, "
"and spare-ratio policy across a dense coastal city. Housing staff compare "
"permit latency, vacancy, and school-capacity constraints. Emergency managers "
"rehearse flood gates, hospital diversion, and radio fallback. "
)
question = (
"Write a 70-word factual paragraph about Paris. Include the Seine, the Louvre, "
"and why it became the capital of France. Do not use bullet points. "
"Do not stop after one word."
)
body = "Use the background notes only as context. Ignore them if they are not needed.\n\n"
while len(body) < 6000:
body += filler
Path("qwen3-4b-long-prompt.txt").write_text(body[:6000] + "\n\n" + question)
print("wrote qwen3-4b-long-prompt.txt", 6000 + 2 + len(question), "chars")
PY

PROMPT="$(cat qwen3-4b-long-prompt.txt)"

trtmc run ./qwen3-4b-instruct-2507.bundle \
--runtime-root "$PWD/build-sm${TRTMC_SM}" \
--prompt "$PROMPT" \
--use-chat-template true \
--enable-thinking false \
--temperature 0 \
--top-k 1 \
--max-new-tokens 64

Mismo prompt en llama.cpp. Lee la línea prompt eval time / Prompt: tok/s, no la vida del proceso.

llama-completion \
-m Qwen3-4B-Instruct-2507-F16.gguf \
-ngl 99 -c 2048 -b 512 -ub 512 -fa on \
--jinja --temp 0 --top-k 1 -n 64 \
--no-warmup --simple-io --no-display-prompt \
--prompt "$PROMPT"

En la J5012 verificada esto es 1,967 tok/s frente a 1,601 tok/s en prefill.

2. Qwen3-0.6B decode codicioso​

Decode es el segundo lugar donde TRTMC lidera, una vez que pides una respuesta larga en una sola petición. Compila un paquete 0.6B FP16 de la misma manera que Qwen3-4B, fijando aún --max-sequence-length 2048. El paquete medido usó un motor dividido de prefill/decode con filas de caché KV=2048.

python -m tensorrt_model_connect build Qwen/Qwen3-0.6B \
--precision fp16 \
--backend trt \
--max-sequence-length 2048 \
--max-batch-size 1 \
--tensor-parallel-size 1 \
--output qwen3-0.6b.bundle \
--verbose

Convierte el mismo snapshot a GGUF F16 para llama.cpp y luego ejecuta dos casos de decode. El llama-cli de esta placa necesita -st (single-turn) y no acepta --prompt-file; pasa --prompt en su lugar.

python3 convert_hf_to_gguf.py Qwen3-0.6B \
--outfile Qwen3-0.6B-F16.gguf \
--outtype f16

Prompt corto, 256 tokens nuevos:

COUNT_PROMPT='Count from 1 to 220. Write only integers separated by spaces. Do not add commentary. Continue until 220.'

trtmc run ./qwen3-0.6b.bundle \
--runtime-root "$PWD/build-sm${TRTMC_SM}" \
--prompt "$COUNT_PROMPT" \
--use-chat-template true \
--enable-thinking false \
--temperature 0 \
--top-k 1 \
--max-new-tokens 256

llama-cli -st --simple-io \
-m Qwen3-0.6B-F16.gguf \
-ngl 99 -c 2048 \
--temp 0 --top-k 1 -n 256 \
--prompt "$COUNT_PROMPT"

Prompt largo, 128 tokens nuevos. El prompt medido fue de 1,212 tokens después de la plantilla de chat: notas sobre alcanzar la órbita y luego una petición de un informe de 180 palabras.

python3 - <<'PY'
from pathlib import Path
note = (
"Low Earth orbit is a crowded working neighborhood for satellites, space stations, "
"and visiting spacecraft. Reaching it requires a launcher that can fight gravity, "
"thinning air, and the need for horizontal speed of about 7.8 kilometers per second. "
"A typical rocket uses staged chemical propulsion. The first stage produces high "
"thrust to clear the dense atmosphere. Upper stages ignite in thinner air to add "
"delta-v. Guidance computers fly a gravity turn, then a circularization burn. "
)
text = "Read the notes below. Then write a detailed 180-word briefing on how rockets reach orbit. Keep writing until the briefing is complete.\n\n"
while text.count(" ") < 900:
text += note
Path("qwen3-06b-long-prompt.txt").write_text(text)
print("wrote", len(text), "chars")
PY

LONG_PROMPT="$(cat qwen3-06b-long-prompt.txt)"

trtmc run ./qwen3-0.6b.bundle \
--runtime-root "$PWD/build-sm${TRTMC_SM}" \
--prompt "$LONG_PROMPT" \
--use-chat-template true \
--enable-thinking false \
--temperature 0 \
--top-k 1 \
--max-new-tokens 128

llama-cli -st --simple-io \
-m Qwen3-0.6B-F16.gguf \
-ngl 99 -c 2048 \
--temp 0 --top-k 1 -n 128 \
--prompt "$LONG_PROMPT"

Lee TRTMC qwen decoder / [trtmc-perf] Decode, y llama.cpp eval time / Generation: tok/s. En el J5012 verificado esto es 69.4 vs 65.5 tok/s para 256 tokens nuevos, y 69.6 vs 63.0 tok/s después del prompt de 1,212 tokens.

Qué significan los números​

  • Menos máquinas. No necesitas una estación de trabajo x86 con GPU solo para exportar ONNX. El Jetson que servirá el modelo también puede construirlo.
  • Menor RAM de host para la conversión que la ruta Edge-LLM FP8 ONNX. Edge-LLM documenta RAM de CPU de hasta unas 20× el tamaño del modelo para la exportación FP8 ONNX. La compilación TRTMC FP16 de Qwen3-4B se mantuvo alrededor de 31 GB usados en el host / 41 GB en el contenedor en Orin 64GB.
  • Prefill largo más rápido en Qwen3-4B que llama.cpp FP16, que es la carga de trabajo donde ayuda un motor TensorRT por lotes.
  • Decode codicioso más rápido en Qwen3-0.6B cuando toda la completion se ejecuta en una sola petición (256 tokens nuevos, o 128 tokens nuevos después de un prompt largo).
  • Un artefacto reutilizable. El .bundle es lo que copias, inspeccionas y ejecutas. No hay un árbol ONNX paralelo que mantener sincronizado.

Solución de problemas​

ProblemaQué comprobar
El contenedor no puede ver la GPUUsa --runtime nvidia y confirma que docker info lista el runtime nvidia
La compilación del motor se queda sin memoriaFija --max-sequence-length 2048, usa AGX Orin 64GB y cierra otros usuarios de la GPU
trtmc run no puede cargar bibliotecasPasa --runtime-root al directorio de compilación de CMake; la CLI no busca DSOs en PATH ni en el directorio actual
La descarga desde Hugging Face fallaColoca un snapshot local en disco y pasa ese directorio a build en lugar del ID del modelo
El decode se ve bien pero el prefill es lentoConfirma que estás en un transformer denso con un motor de prefill de doble perfil / por lotes, no en un grafo híbrido Mamba
TRTMC parece más lento que llama.cppCompara el tiempo del motor ([trtmc-perf], qwen decoder, llama.cpp eval time), no el tiempo de pared del proceso. Mantén el modelo cargado y genera muchos tokens en una sola petición
llama-cli da errores con --prompt-file o espera stdinUsa -st --simple-io --prompt "..."
Falta nvidia-smi en JetsonEstablece SM=87 para AGX Orin

Recursos​

Soporte técnico y debate sobre el producto​

Gracias por elegir nuestros productos. Estamos aquí para ofrecerte distintos tipos de soporte y garantizar que tu experiencia con nuestros productos sea lo más fluida posible. Ofrecemos varios canales de comunicación para adaptarnos a diferentes preferencias y necesidades.

Loading Comments...