环境:从零搭建了整个Orin nx 上的环境,包括tensorrt,trex, tensorrt-edge-llm。

模型实战:测试模型最佳性能,首先需要开启MAXN模式,加锁定GPU频率,然后根据测试的模型是tensorrt-edge-llm支持的模型,还是普通网络模型来抉择测试方法。测试完后调回默认频率。

VLM大模型:tensorrt-edge-llm

普通网络模型:trtexec+polygraphy

瓶颈分析:

一、Orin NX 烧录

主机(ubuntu20.04)通过 sdk manager 烧录 orin nx 开发板,下面是辅助刷写的主机版本和支持刷写的Jetpack版本。可见ubuntu20.04支持jetpack6.2.x

官网下载主机(ubuntu)对应的sdk-manager:SDK Manager | NVIDIA Developer

sudo dpkg -i cuda-keyring_1.1-1_all.deb

sdkmanager

登陆账号,选择jetpack版本进行安装和刷写,刷写过程中需要设置orin nx 板端账号和密码,Storage Device 选 NVME(没有就选default),Target Proxy Settings 选 Do not set proxy.

刷写完成后显示docker runtime 下载失败,但是不影响我们使用orin.

二、浏览器、中文设置、conda安装、torch下载、换源

2.1 firefox浏览器安装

Orin 上 snap2.70容易引发崩溃,特别是firefox浏览器会无法打开,一直转圈。为了阻止崩溃,所以一开始直接对snap降版本,并阻止其更新。

参考:

jetson orin nx ubuntu系统更新后firefox 无法启动_orin nx安装firefox-CSDN博客文章浏览阅读678次,点赞5次,收藏8次。jetson orin nx ubuntu系统更新后firefox 无法启动_orin nx安装firefox https://dongpan90.blog.csdn.net/article/details/150430359?fromshare=blogdetail&sharetype=blogdetail&sharerId=150430359&sharerefer=PC&sharesource=qq_56719965&sharefrom=from_link

# 下载指定版本的Snapd
snap download snapd --revision=24724

# 安装并锁定版本
sudo snap ack snapd_24724.assert
sudo snap install snapd_24724.snap
sudo snap refresh --hold snapd

# 安装 firefox
sudo apt-get install firefox

2.2 orin 设置中文

jetpack 6.2.1 是 ubuntu_22.04+cuda_12.6的系统

参考:

为 Ubuntu22.04 系统添加中文输入法_ubuntu22.04中文输入法-CSDN博客文章浏览阅读5.1w次,点赞93次,收藏299次。本文详细介绍了如何在Ubuntu22.04操作系统中添加中文输入法。首先,通过安装Chinese语言包,然后在Settings的Region&Language选项中管理已安装的语言。接着,使用终端命令安装ibus-pinyin输入法引擎,并在Keyboard设置中添加Chinese(IntelligentPinyin)输入源。完成后,用户可以方便地切换中英文输入法。 https://blog.csdn.net/weixin_44916154/article/details/124582379?fromshare=blogdetail&sharetype=blogdetail&sharerId=124582379&sharerefer=PC&sharesource=qq_56719965&sharefrom=from_link注意:在 sudo apt-get install ibus-pinyin 之后直接重启就可以使用拼音了。

2.3 arm conda 安装

在Jetson Orin NX上,由于系统是基于 aarch64 架构的,官方Anaconda并不直接支持。因此,最佳方案是使用专为ARM架构设计的 Miniforge。

进入官网进行下载:Releases · conda-forge/miniforge · GitHub

JetPack6.2自带的python是3.10,为了后续使用tensorrt工具链顺利,创建的conda环境也需要是3.10.

2.4 arm torch 安装

NVIDIA官方为不同JetPack版本提供的PyTorch预编译包(.whl文件)

可以下载官网jetpack6.2+cuda12.6推荐的torch版本:jp6/cu126 index

下载 torch-2.11.0 、torchvision-0.26.0 、torchaudio-2.10.0

更多版本:jp6/cu126: simple list (including inherited indices)

官方的torch下载速度非常非常慢!!!!!!!!!!!!!!!!!!!!!!!

2.5 Orin NX ubuntu22.04 换源

2.5.1 apt 换源

给 Jetson Orin 上的 Ubuntu 22.04 换源,核心是将默认源地址替换为国内镜像站。需要注意,Jetson 是 ARM 架构,必须使用 ports.ubuntu.com 这个专门为 ARM 设备准备的源,不能直接用 x86 。

参考:Jeston Orin NX Ubuntu22.04 安装ROS2 humble_jetson 22.04 换源-CSDN博客

# 备份原始源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

# 添加中科大镜像
# 使用 sed 命令一键替换,针对 Ubuntu 22.04 (Jammy) 系统
sudo sed -i 's|http://ports.ubuntu.com/ubuntu-ports/|https://mirrors.ustc.edu.cn/ubuntu-ports/|g' /etc/apt/sources.list

# 更新源
sudo apt update
2.5.2 pip 换源
cd ~
mkdir ~/.pip
sudo gedit ~/.pip/pip.conf
# 写入以下内容
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host =
    pypi.tuna.tsinghua.edu.cn
# 保存退出

三、TensorRT环境安装

3.1 启用 Orin NX 自身的 tensorrt

(若自行下载tensorrt工具链:TensorRT 10.x Download | NVIDIA Developer)

参考:

jetson orin nx找到自带的tensorrt_jetson orin tensorrt-CSDN博客文章浏览阅读1.1k次。分别是/usr/src/tensorrt/bin/trtexec和/usr/src/tensorrt/sample/trtexec。如果这时候进入/usr/src/tensorrt/bin文件夹后使用trtexec命令显示command not found。ok,这次我们再输入trtexec --help或转engine文件就可以了。这里你自己的路径是什么就写什么,然后保存。我们就手动加上环境变量。_jetson orin tensorrt https://blog.csdn.net/cyh20182808/article/details/141856325?fromshare=blogdetail&sharetype=blogdetail&sharerId=141856325&sharerefer=PC&sharesource=qq_56719965&sharefrom=from_link查看orin nx 自带的tensorrt 

# 安装jetpack
sudo apt install nvidia-jetpack

# 查看 jetpack 版本
dpkg -l | grep tensorrt

查找trtexec路径,并添加到环境变量中

# 查看tenorrt路径
sudo find /usr -name trtexec 

sudo gedit ~/.bashrc

export PATH=$PATH:/usr/src/tensorrt/bin
export PATH=$PATH:/usr/src/tensorrt/samples

source ~/.bashrc

查看 tensorrt 命令是否存在,输出参数行代表命令正常运行:

trtexec -h

3.2 安装 TensorRT OSS

TensorRT OSS仓库主要包含以下开源组件:

  • 官方插件 (Plugins):TensorRT官方实现的一系列预定义操作(如:CustomSlice, InstanceNormalization等)。某些复杂的网络结构必须依赖这些插件才能运行。

  • 模型解析器 (Parsers):将其他格式的模型转换为TensorRT引擎的工具,主要包括:

    • ONNX Parser:解析ONNX格式的模型。

    • Caffe Parser:解析Caffe格式的模型。

  • 示例应用 (Sample Applications):展示如何使用TensorRT C++/Python API进行推理的示例代码

下载本地 tensorrt 对应版本的 tensorrt oss:https://github.com/NVIDIA/TensorRT

安装基础的 cuda torch 环境

# 首先创建 conda 环境
conda create --name TensorRTOSS python=3.10.12 
conda activate TensorRTOSS

# 将 Conda lib 路径添加到 LD_LIBRARY_PATH 是解决 C++ 库版本不匹配问题
#echo 'export LD_LIBRARY_PATH=$CONDA_PREFIX/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
#source ~/.bashrc
conda env config vars set LD_LIBRARY_PATH=$CONDA_PREFIX/lib:$LD_LIBRARY_PATH

# 安装 torch 环境
pip install torch-2.11.0-cp310-cp310-linux_aarch64.whl
pip install torchaudio-2.10.0-cp310-cp310-linux_aarch64.whl
pip install torchvision-0.26.0-cp310-cp310-linux_aarch64.whl

为了利用 trt-engine-explorer 进行模型转换及模型性能可视化,需要安装下面两个模型:

安装 pytorch-quantization

cd TensorRT-10.6.0/tools/pytorch-quantization/

# Install PyTorch and prerequisites
pip install -r requirements.txt

# jupyter 画图阶段会出现报错: MoudleNotFoundError:NO module named 'pkg_rescources'
# 解决方法:提前对 setuptools 降级
pip install setuptools==59.6.0

# build
python setup.py install

在进行 python setup.py install 遇到报错:缺少cudss依赖

ImportError: libcudss.so.0: cannot open shared object file: No such file or directory

如果是torch2.5.0则会报错:缺少 cusparseLt库

ImportError: libcusparseLt.so.0: cannot open shared object file: No such file or directory

解决方法:Help me with Correct Pytorch and Torchvision versions requirement for Jetpack 6.2.1 orin super - Jetson Orin Nano - NVIDIA Developer Forums

jetpack6.2.1默认不包含cudss和cusparseLt,需手动下载:

cudss:Index of /compute/cudss/redist/libcudss/linux-sbsa

cusparseLt:Index of /compute/cusparselt/redist/libcusparse_lt/linux-aarch64

官方文档:

cusparseLt:cuSPARSELt: A High-Performance CUDA Library for Sparse Matrix-Matrix Multiplication — NVIDIA cuSPARSELt

cudss:NVIDIA cuDSS (Preview): A high-performance CUDA Library for Direct Sparse Solvers — NVIDIA cuDSS

# 将下载的文件解压,并把头文件和库文件复制到系统的 CUDA 目录中
tar xf ${CUSPARSE_SOLVER_NAME}.tar.xz
sudo cp -a ${CUSPARSE_SOLVER_NAME}/include/* /usr/local/cuda/include/
sudo cp -a ${CUSPARSE_SOLVER_NAME}/lib/* /usr/local/cuda/lib64/

# 更新系统库缓存
sudo ldconfig

# 验证安装
ls /usr/local/cuda/lib64 | grep cudss

# 测试pytorch
python3 -c "import torch; print(torch.__version__)"

安装 trt-engine-explorer

cd TensorRT/tools/experimental/trt-engine-explorer

# 以开发模型安装 trex
python3 -m pip install -e .[notebook]

# 安装 Graphviz
sudo apt --yes install graphviz

# 以下是针对后续使用jupyterlab 会出现的各种问题的解决方法
# dash 依赖版本不兼容,进行版本降级
pip install dash==1.21.0 werkzeug==2.0.3 dash-bootstrap-components==0.13.1 dash_daq==0.5.0 dash_colorscales==0.0.4

# 检查是否能进入jupyter notebook
jupyter lab

ERROR:No matching distribution found for pynvx

pynvx 官方并未提供适用于 ARM 架构(如 aarch64)的预编译 wheel 文件。pip install pynvx 会尝试从 PyPI 下载适合当前平台的版本,但对 aarch64 架构,它找不到对应的 wheel,因此会失败。

源码编译pynvx,编译过程会在 Jetson 设备上生成适配 aarch64 架构的版本。

# 下载 pynvx 和  pybind11
git clone https://github.com/helloall1900/pynvx.git
cd pynvx
git clone https://github.com/pybind/pybind11.git pybind11

# 编译
pip install .

再次执行python3 -m pip install -e .[notebook],成功。

TensorRT OSS 使用

!!!已打包为bash脚本,路径为 /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine目录下 build_engine_llm.sh 和 build_engine_visual.sh

前景提要:使用 TensorRT-Edge-LLM 导出的 onnx 模型 进行模型转换,目的是为了方便使用trex进行模型可视化分析。

如果报错找不到 tensorrt,则将系统包路径添加到 Python 路径中。

# 找到系统 TensorRT 安装路径
dpkg -L python3-libnvinfer | grep -E 'dist-packages|site-packages'

# 临时设置
export PYTHONPATH=/usr/lib/python3.10/dist-packages:$PYTHONPATH

# 用trtexec 转换 tensorrt-edge-llm 导出的模型会报错 trt 找不到 llm 自定义的插件
# 直接导出后的模型利用 trtexec 量化
python3 /home/pub/TensorRT-release-10.3/tools/experimental/trt-engine-explorer/utils/process_engine.py \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/origin_onnx/llm/model.onnx \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/llm \
int8 \
preview=+aliasedPluginIO1003

# 量化后导出,trtexex转换
# llm部分
python3 /home/pub/TensorRT-release-10.3/tools/experimental/trt-engine-explorer/utils/process_engine.py \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/onnx/llm/model.onnx \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/llm \
stronglyTyped \
preview=+aliasedPluginIO1003
# shapes=

# visual部分 设置tokens=1000
python3 /home/pub/TensorRT-release-10.3/tools/experimental/trt-engine-explorer/utils/process_engine.py \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/onnx/visual/model.onnx \
/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/tokens1000/visual \
stronglyTyped  \
preview=+aliasedPluginIO1003 \
shapes=input:1000x1536,rotary_pos_emb:1000x32,cu_seqlens:2,max_seqlen_carrier:1000,fast_pos_embed_idx:4x1000,fast_pos_embed_weight:4x1000


# 动态模型参数设置,当input数量过多时,不设置Shapes,让tretexec自行选择
# 对于Qwen3模型,有llm 和 visual 部分
# LLM部分:主要动态维度通常只有序列长度(sequence_length) 一个。
# visual部分:除了批次大小(batch),还可能涉及图像高度(H)、宽度(W) 等多个动态维度,这增加了自动推导的难度。
# --minShapes=inputs_embeds:1x1000x2048 \
# --optShapes=inputs_embeds:1x1000x2048 \
# --maxShapes=inputs_embeds:1x1000x2048

# 依旧报插件问题
export LD_PRELOAD=/home/pub/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

# 遇到报错:[E] Error[4]: IBuilder::buildSerializedNetwork: Error Code 4: Internal Error (Aliased I/O used by plugin n0 but PreviewFeature::kALIASED_PLUGIN_IO_10_03 not enabled.)
# 设置 --preview=+aliasedPluginIO1003 ,启用 aliasedPluginIO1003 预览功能


# 示例
python3 /home/pub/TensorRT-release-10.3/tools/experimental/trt-engine-explorer/utils/process_engine.py /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/onnx/visual/model.onnx /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/tokens1000/visual  stronglyTyped  preview=+aliasedPluginIO1003  shapes=input:1000x1536,rotary_pos_emb:1000x32,cu_seqlens:2,max_seqlen_carrier:1000,fast_pos_embed_idx:4x1000,fast_pos_embed_weight:4x1000

python3 /home/pub/TensorRT-release-10.3/tools/experimental/trt-engine-explorer/utils/process_engine.py /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/onnx/llm/model.onnx /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/tokens1000/llm  stronglyTyped  preview=+aliasedPluginIO1003  shapes=inputs_embeds:1x1000x2048,rope_rotary_cos_sin:1x1000x128,context_lengths:1,kvcache_start_index:1,last_token_ids:1x1,deepstack_embeds_0:1x1000x2048,deepstack_embeds_1:1x1000x2048,deepstack_embeds_2:1x1000x2048,past_key_values_0:1x2x8x0x128,past_key_values_1:1x2x8x0x128,past_key_values_2:1x2x8x0x128,past_key_values_3:1x2x8x0x128,past_key_values_4:1x2x8x0x128,past_key_values_5:1x2x8x0x128,past_key_values_6:1x2x8x0x128,past_key_values_7:1x2x8x0x128,past_key_values_8:1x2x8x0x128,past_key_values_9:1x2x8x0x128,past_key_values_10:1x2x8x0x128,past_key_values_11:1x2x8x0x128,past_key_values_12:1x2x8x0x128,past_key_values_13:1x2x8x0x128,past_key_values_14:1x2x8x0x128,past_key_values_15:1x2x8x0x128,past_key_values_16:1x2x8x0x128,past_key_values_17:1x2x8x0x128,past_key_values_18:1x2x8x0x128,past_key_values_19:1x2x8x0x128,past_key_values_20:1x2x8x0x128,past_key_values_21:1x2x8x0x128,past_key_values_22:1x2x8x0x128,past_key_values_23:1x2x8x0x128,past_key_values_24:1x2x8x0x128,past_key_values_25:1x2x8x0x128,past_key_values_26:1x2x8x0x128,past_key_values_27:1x2x8x0x128

llm的输入很多,可以写个python脚本来生成

# 生成形状字典
shapes = {
    "inputs_embeds": "1x1000x2048",
    "rope_rotary_cos_sin": "1x1000x128",
    "context_lengths": "1",
    "kvcache_start_index": "1",
    "last_token_ids": "1x1",
    "deepstack_embeds_0": "1x1000x2048",
    "deepstack_embeds_1": "1x1000x2048",
    "deepstack_embeds_2": "1x1000x2048",
}

# 添加 28 个 past_key_values
for i in range(28):
    shapes[f"past_key_values_{i}"] = "1x2x8x0x128"

# 生成 --shapes 参数
shape_str = ",".join(f"{k}:{v}" for k, v in shapes.items())
print(shape_str)

ERROR:Cannot find plugin : AttentionPlugin

解决方法:

export LD_PRELOAD=/home/pub/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

[E] Error[4]: IBuilder::buildSerializedNetwork: Error Code 4: Internal Error (Aliased I/O used by plugin n0 but PreviewFeature::kALIASED_PLUGIN_IO_10_03 not enabled.)

解决方法:

设置 --preview=+aliasedPluginIO1003 ,启用 aliasedPluginIO1003 预览功能

交互式可视化分析

通过 trt-engine-explorer/utils/process_engine.py 可以得到模型引擎及性能文件

利用 jupyter lab 生成交互式可视化分析报告

首先进入 jupyter lab 界面

conda activate TensorRTOSS

jupyter lab

最好在性能分析文件保存路径下进行分析,生成report_card_visual(llm).ipynb

import IPython
from ipywidgets import widgets
from trex import *
from trex.notebook import *
from trex.report_card import *

engine_name = "/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/tokens900/visual/model.onnx.engine"

plan = EnginePlan(f"{engine_name}.graph.json", f"{engine_name}.profile.json", f"{engine_name}.profile.metadata.json")

# 设置 Pandas 显示无限制
import pandas as pd
pd.set_option('display.max.columns',None)

# 打印所有列
print(plan.df.columns.tolist())

print('===================================================================')
# 打印所有层
df = plan.df
print(df['Name'])

print('===================================================================')
# 打印最慢的几层
top3 = plan.df.nlargest(3, 'latency.pct_time')
for i in range(len(top3)): 
    layer = top3.iloc[i] 
    print("%s: %s" % (layer["Name"], layer["subtype"]))

print('===================================================================')
# 模型结构图
#report_card_draw_plan_graph_extended(plan, engine_name)

# 模型的结构,性能总结
plan.summary()

# 由于当前模型属性type为subtype
# 需要修改 /tools/experimental/trt-engine-explorer/trex/report_card.py 中的'type' 替换为 'subtype'
# 不同类型 Layer 的延时占比图
layer_latency_sunburst(plan.df, "Layers Latencies (%)")

# plan 的各个属性打印成表格
# report_card_table_view(plan)

# 多次推理后的延迟分布图,前期推理时不稳定,后期逐步稳定延迟接近一条直线 ,我自己推测可以考虑是否没设置warmUp
plot_engine_timings(timing_json_file= f"{engine_name}.timing.json")

# Performance
# 延迟和精度的汇总卡
report_card_perf_overview_widget(plan)

# Memory Footprint
# 内存占比,分权重和激活
report_card_memory_footprint_widget(plan)

# Convolutions 
# 但是没有卷积层
#convs = plan.get_layers_by_type('Convolution')
#report_card_convolutions_overview_widget(convs)

四、TensorRT-Edge-LLM环境安装

简介

TensorRT Edge-LLM 是 NVIDIA 在嵌入式平台上为 LLM 和 VLM 提供高性能的 C++ 推理运行时。定量(INT4、NVFP4、FP8)可减少重量占用,使大型型号在受内存约束的设备上保持实用性。

Edge-LLM 支持从 Orin Nano 到 Thor 等整个 Jetson 系列的多种 LLM 和 VLM 系统。

参考:TensorRT Edge-LLM on Jetson | Jetson AI Lab

官网下载:https://github.com/NVIDIA/TensorRT-Edge-LLM

官方文档:Installation — TensorRT Edge-LLM

安装TensorRT-Edge-LLM

Part 1: Quantization and tensorrt_edgellm (x86 Host with GPU / Edge Device)

从 PyTorch 2.11 版本开始,官方已经支持在 aarch64 架构的 Linux 系统上,直接从 PyPI 安装包含 CUDA 支持的 wheel 包了,所以直接安装 tensorrt-edge-llm-v_0.8.0

Verify Your Prerequisites:

# Check CUDA installation
nvcc --version
# Should show CUDA 12.x or 13.x

# Check GPU and available memory
nvidia-smi
# Look for GPU memory (e.g., "24576MiB" for 24GB)

# Check Python version
python3 --version
# Should show Python 3.10 or higher

下载资源库

git clone https://github.com/NVIDIA/TensorRT-Edge-LLM.git
cd TensorRT-Edge-LLM
# 初始化 --progress 显示实际下载速度和剩余大小
git submodule update --init --recursive --progress

安装依赖

注意修改 requirements.txt 中 numpy==2.2.6,

pyproject.toml 中 numpy==2.2.6,torch==2.11.0,torchvision==0.26.0

pip安装的torch2.12,torchvision=0.27.0是cuda13编译,编译可以成功,但是量化阶段会报错cuda内核不匹配

conda create --name  TensorRTLLM python=3.10.12
conda actiavte TensorRTLLM

# 安装 适配jetpack6.2.1 cuda12.6的 torch 环境
pip install torch-2.11.0-cp310-cp310-linux_aarch64.whl
pip install torchaudio-2.10.0-cp310-cp310-linux_aarch64.whl
pip install torchvision-0.26.0-cp310-cp310-linux_aarch64.whl

# Required for checkpoint export
# requirements.txt 里 numpy==2.4.6 改numpy==2.2.6
pip3 install -r requirements.txt

# Required for quantization, LoRA merge, vocabulary reduction, audio preprocessing,
# and tokenizer helpers
# 需要修改 pyproject.toml 中的 numpy==2.2.6,torch==2.11.0,torchvision==0.26.0
pip3 install ".[tools]"

# Required only for the experimental high-level Python API and server
pip3 install -r requirements-server.txt

当 numpy==2.4.6 时,报错:

设置环境变量,并验证

# export EDGE_LLM_PATH=/path/to/TensorRT-Edge-LLM
# export PYTHONPATH=$EDGE_LLM_PATH:$PYTHONPATH

export EDGE_LLM_PATH=/home/pub/TensorRT-Edge-LLM
export PYTHONPATH=$EDGE_LLM_PATH:$PYTHONPATH

# Verify the recommended quantization, export, LoRA, and vocabulary tools
tensorrt-edgellm-quantize --help
tensorrt-edgellm-export --help
tensorrt-edgellm-merge-lora --help
tensorrt-edgellm-reduce-vocab --help

Part 2: C++ Runtime (Edge Device)

jestson 设备与 对应 jetpack版本 的支持精度

系统需求

当前设备为:Jetson Orin + JetPack 6.2.1 + cuda12.6 

JetPack 6.2 + Orin:

mkdir -p build
cd build

cmake .. \
    -DCMAKE_BUILD_TYPE=Release \
    -DTRT_PACKAGE_DIR=/usr \
    -DCMAKE_TOOLCHAIN_FILE=cmake/aarch64_linux_toolchain.cmake \
    -DEMBEDDED_TARGET=jetson-orin \
    -DCUDA_CTK_VERSION=12.6 \
    -DENABLE_CUTE_DSL=OFF
    # -DENABLE_CUTE_DSL=ALL 容易在build时报错CUTE_DSL链接不到cuda动态库

Building on x86 GPU Systems (Optional for Developers)

如果要在使用 NVIDIA GPU 的 x86 工作站上进行构建和测试(在部署到 Edge 设备之前需要开发),可以改用以下配置:

mkdir -p build
cd build

cmake .. \
    -DCMAKE_BUILD_TYPE=Release \
    -DTRT_PACKAGE_DIR=/path/TensorRT-10.x.x \
    -DCUDA_CTK_VERSION=<YOUR_CUDA_VERSION> \
    -DENABLE_CUTE_DSL=ALL

Build Project & Verify Build

# Build Project
make -j$(nproc)

# Verify Build
# Test C++ examples
./examples/llm/llm_build --help
./examples/llm/llm_inference --help

设置-DENABLE_CUTE_DSL=ALL后编译会报错,没有找到解决方法,所以设置为-DENABLE_CUTE_DSL=OFF

ERROR:CUTE_DSL库未正确链接CUDA动态库

插件依赖

但凡在使用其他工具加载tensorrt-edge-llm编译的时候,插件依赖出问题:

比如

直接引用插件就可解决

export LD_PRELOAD=/home/pub/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

LLM导出模型、量化模型(x86 / Jetson)

TensorRT-Edge-LLM 的 tensorrt-edgellm-quantize llm 命令,默认只量化 LLM 文本解码器主干,完全不触碰视觉分支权重。

若需要量化视觉分支,添加参数--visual_quantization fp8,并且使用多模态校准数据集:--dataset lmms-lab/MMMU

(由于 Orin nx 不支持 fp8,所以这里只需做 llm 量化即可 )

这里以Qwen3-VL-2B-Instruct模型为例

cd ..

# 设置环境变量
export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM
export MODEL_NAME=Qwen3-VL-2B-Instruct
export PYTHONPATH=/home/pub/TensorRT-Edge-LLM:$PYTHONPATH

# 设置 hugging face 镜像链接
export HF_ENDPOINT=https://hf-mirror.com
pip install huggingface-hub==1.5.0

# Export FP16 model to ONNX
tensorrt-edgellm-export \
    Qwen/Qwen3-VL-2B-Instruct \
    $MODEL_NAME/onnx

# Or quantize an FP16/BF16 model first, then export the quantized checkpoint
tensorrt-edgellm-quantize llm \
    --model_dir Qwen/Qwen3-VL-2B-Instruct \
    --output_dir $MODEL_NAME/quantized \
    --quantization int8_sq \ 
    # --lm_head_quantization nvfp4 
# --quantization: Backbone orin 只支持int8_q 、int4_awq
# --lm_head_quantization: LM head orin 只支持 int4_awq,不指定时,跟随 quantization 精度

tensorrt-edgellm-export \
    $MODEL_NAME/quantized \
    $MODEL_NAME/onnx

# Or export a pre-quantized INT4-AWQ checkpoint
# See https://huggingface.co/Qwen/Qwen3-4B-AWQ
tensorrt-edgellm-export \
    /path/to/Qwen/Qwen3-4B-AWQ \
    Qwen3-4B-AWQ/onnx \
 #   --externalize-weights int4_ffn 如果内存够,不要设置这个参数,
#会导致性能测试时,TensorRT 引擎在推理时找不到输入张量 _model.model.layers.0.mlp.gate_proj.qweight 的内存地址

当模型为qwen2-vl时,直接使用 tensorrt-edgellm-export 只能导出 llm 部分,因为 tensorrt-edge-llm 对 qwen3_vl 内置支持一体化导出(自动导出 llm+visual); 对 qwen2_vl 同样不会自动导出 visual,并且 没有封装 ViT 独立导出接口。

执行导出命令时可以看到 visual :no,说明 qwen2_vl 不在 tensorrt-edge-llm 支持的视觉模型白名单上。

查看TensorRT-Edge-LLM/build/lib/tensorrt_edgellm/onnx/export_encoder.py 可以发现 TensorRT-Edge-LLM v0.8.0 支持 以下视觉模型自动导出visual部分。

tensorrt-edgellm-export 是控制台入口脚本,底层指向源码:tensorrt_edgellm/scripts/export.py。基本上export_encoder.py 支持的模型,都在白名单内。不支持的模型,无法借助当前工具进行导出,tensorrt-edgellm-export-visual 手动调用 export_visual_onnx 进行导出这条路走不通。

推荐安装旧版本 tensorrt-edge-llm,v0.7.0版本以下支持qwen2_vl

安装旧版本tensorrt-edge-llm之后,成功导出未量化的qwen2_vl视觉部分,下载Qwen2-VL-2B-Instruct-AWQ,查看config文件也可以看到,视觉部分是跳过量化的。

模型量化时的环境报错:

ERROR:pip 安装了一个为 CUDA 13 编译的 PyTorch 版本

(此时torch为默认的torch2.12,torchvision=0.27.0)

报错:

huggingface_hub.errors.HfUriError: Invalid HF URI 'hf://datasets/cnn_dailymail@96df5e686bee6baa90b8bee7c28b81fa3fa6223d/.huggingface.yaml'. Repository id must be 'namespace/name', got 'cnn_dailymail'.

解决方法:

pip install huggingface-hub==1.5.0

LLM转换模型(Jetson)

# 设置环境变量
export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM
export MODEL_NAME=Qwen3-VL-2B-Instruct
export PYTHONPATH=/home/pub/TensorRT-Edge-LLM:$PYTHONPATH

# Build language model engine
./build/examples/llm/llm_build \
  --onnxDir $WORKSPACE_DIR/$MODEL_NAME/onnx/llm \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm \
  --maxBatchSize 1 \
  --maxInputLen 1024 \
  --maxKVCacheCapacity 4096

# Build visual encoder engine
./build/examples/multimodal/visual_build \
  --onnxDir $WORKSPACE_DIR/$MODEL_NAME/onnx/visual \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines \
  --minImageTokens 128 \
  --maxImageTokens 1024 \
  --maxImageTokensPerImage 1024

# 为了测试模型,将 maxImageTokensPerImage 和 maxImageTokens 改为 1024
# tensorrt oss 可视化 用的还是 512

转换模型 参数一览

--profilingDetailed:仅在需要获取更细粒度的性能分析(如每个 ONNX op 的耗时)时开启,不影响功能

LLM模型推理(Jetson)

创建一个输入文件 $WORKSPACE_DIR/input_vlm.json,替换图片路径

{
    "batch_size": 1,
    "temperature": 1.0,
    "top_p": 1.0,
    "top_k": 50,
    "max_generate_length": 128,
    "requests": [
        {
            "messages": [
                {
                    "role": "system",
                    "content": "You are a helpful assistant."
                },
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "image",
                            "image": "/path/to/image.jpg"
                        },
                        {
                            "type": "text",
                            "text": "Please describe the image."
                        }
                    ]
                }
            ]
        }
    ]
}

开始推理

./build/examples/llm/llm_inference \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm \
  --multimodalEngineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual \
  --inputFile $WORKSPACE_DIR/vlm_test/input_vlm.json \
  --outputFile $WORKSPACE_DIR/vlm_test/output_vlm.json

LLM基准测试

llm_inference 是端到端推理的示例。在运行真正的 JSON 请求、应用聊天模板、生成文本、编写响应式 JSON 以及验证应用程序级行为时,请使用它。它还可以报告整体绩效指标,例如代币/秒。

llm_bench 是基准示例。在需要为内置引擎使用合成预填充/解码时,无需准备输入的JSON文件时使用。默认情况下,它会报告整体E2E计时;通过--profile收集内核级别崩溃的每层分析。

./build/examples/llm/llm_bench \
    --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines \
    --mode prefill \
    --inputLen 128 \
    --batchSize 1

参数解释

了解各Transformer模型结构,如图像 patch_size,merge_size参考

transformers/src/transformers/models/qwen3_vl/configuration_qwen3_vl.py at v5.14.1 · huggingface/transformers · GitHub

类别参数说明必需/默认
必需选项--engineDir <dir>TensorRT 引擎目录路径必需
--mode <mode>基准测试模式,可选值:prefill、decode、spec_verify、spec_draft_proposal、spec_draft_prefill、visual必需
模式特定选项prefill 模式
--inputLen输入序列长度必需
--reuseKVLen复用的 KV 缓存长度可选,默认 0
decode 模式
--pastKVLen

历史 KV 缓存长度;

解码开始时的初始缓存长度。

必需
spec_verify 模式
--verifyTreeSize验证树大小必需
--pastKVLen历史 KV 缓存长度必需
spec_draft_proposal 模式
--draftTreeSize草稿树大小必需
--pastKVLen历史 KV 缓存长度必需
spec_draft_prefill 模式
--inputLen输入序列长度必需
--reuseKVLen复用的 KV 缓存长度可选,默认 0
visual 模式
--engineDir视觉编码器引擎目录(注意:会覆盖主 --engineDir)必需
--imageSize <HxW>

图像尺寸,例如 896x448。
视觉 Token 数 = (grid_h × grid_w) / (merge_size)²

grid_h = 图像高度 / patch_size

grid_w = 图像宽度 / patch_size

qwen3: patch_size=16, merge_size=2

必需
通用选项--help显示帮助信息可选
--debug启用调试模式(详细日志)可选
--batchSize批处理大小可选,默认 1
--iterations性能分析迭代次数(预热后)可选,默认 10
--warmup预热迭代次数可选,默认 3
--osl输出序列长度,用于 decode 端到端计时。
osl=1:E2E 运行 --iterations 次,单个 Token 的生成延迟(Latency)。
osl>1:E2E 运行完整序列解码一次,运行 1 次连续解码,包含 osl-1 个步骤(Prefill 已产生第一个 token)。输出 Token 生成速率 Generation(22.7 tok/s)。
可选,默认 1
--profile启用逐层性能分析(默认禁用)可选
--outputDir输出 CSV 文件的目录(层分析和 E2E 计时)可选
--seed随机种子,用于可复现数据可选,默认 0
--noCudaGraph禁用 decode/EAGLE E2E 计时的 CUDA Graph 捕获。
默认启用,失败时会回退到非 Graph 模式
可选
--extractLayerInfo <opts>逗号分隔的层信息提取选项:
all、shapes、onnx_ops、tactics、data_types
使用此选项会隐含启用 --profile
可选
推测解码选项--acceptRate每次推测解码迭代平均接受的 token 数(默认 5)。
verify_steps = ceil((osl-1) / acceptRate)
可选,默认 5
--draftStep每次推测解码迭代的草稿步数(默认 6)。
draft_calls = verify_steps * (draftStep-1)
可选,默认 6

llm decode 的性能测试

使用tensorrt-edge-llm llm_bench工具进行性能测试

./build/examples/llm/llm_bench     \
--engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm/     \
--mode decode     \
--pastKVLen 10 \
--osl 100    \
--batchSize 1  \
--outputDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm.csv

llm decode 性能

模型模式批处理大小输出序列长度 (osl)端到端时延 (ms)平均Token时延 (ms)吞吐量 (tokens/s)KV缓存长度
Decode11004377.3444.2222.6210

完整的终端输出为:

qwen3 在 orin nx 16G 上的性能基准

Qwen 模型命名结构
结构部分含义与说明典型示例
系列与版本标识模型所属的产品线和迭代代际,是理解模型能力的基础。Qwen (初代), Qwen1.5, Qwen2, Qwen2.5, Qwen3
参数量表示模型的参数规模,B 代表十亿(Billion),M 代表百万(Million),这是衡量模型大小的核心指标。1.5B, 7B, 72B;MoE模型如 30B-A3B (总参数量300亿,激活参数30亿)
能力/模态指明模型专精的任务领域或能处理的数据模态,是选型的关键。-VL (视觉语言多模态), -Coder (代码生成), -Math (数学推理)
微调类型表示模型在预训练后进行的特定对齐训练,决定了使用方式。-Instruct (指令微调,可直接对话), -Base (基础模型,需二次微调), -Chat (对话优化), -Thinking (深度思考模式)
量化方法/格式位于文件名末尾的标签,说明模型为部署而采用的压缩技术和文件格式。-GPTQ-Int4 (GPTQ算法的4-bit量化), -AWQ (AWQ算法的4-bit量化), .gguf (GGUF格式,含Q4_K_M, IQ3_M等量化配置)
VLM 大模型量化方法
特性INT4 AWQINT8 SmoothQuantGPTQ-Int4GPTQ-Int8
核心思路保护激活值大的“重要”权重通道,减少精度损失将量化难度从激活值“平滑”迁移到权重上,实现权重和激活同时量化通过逐层最小化输出误差,用优化方法寻找最佳权重原理同GPTQ,但量化位宽为8-bit
量化对象仅权重量化 (W4A16)权重+激活量化 (W8A8)仅权重量化 (W4A16)仅权重量化 (W8A16)
模型压缩比极高 (~4x)中等 (~2x)极高 (~4x)中等 (~2x)
精度保留高,通常优于GPTQ-Int4很高,是INT8方案中的优等生良好,是经典的4-bit方案高,精度损失很小
推理速度 (小Batch)快,内存带宽是瓶颈时优势明显快,得益于INT8 Tensor Core快快
推理速度 (大Batch)相对变慢优势明显,计算密度成为关键相对变慢相对变慢
部署兼容性TensorRT-LLM, llama.cpp, HuggingFaceTensorRT-LLM, ONNX Runtime, vLLMTensorRT-LLM, AutoGPTQ, vLLM, HuggingFaceTensorRT-LLM, AutoGPTQ, vLLM, HuggingFace
校准数据需求较少较多中等中等
典型应用场景边缘设备、本地部署、极致显存优化数据中心、追求高吞吐和高精度平衡通用、生态成熟、社区支持广泛通用、精度敏感型任务
命令
conda activate TensorRTLLM

export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM
export MODEL_NAME=Qwen3-VL-2B-Instruct
export PYTHONPATH=/home/pub/TensorRT-Edge-LLM:$PYTHONPATH

# visual encoder
./build/examples/llm/llm_bench     --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual   --mode visual   --batchSize 1  --imageSize=960x960

# llm profil
./build/examples/llm/llm_bench     --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm/     --mode prefill     --inputLen 512  --batchSize 1

# llm decoder
./build/examples/llm/llm_bench     --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm/     --mode decode     --pastKVLen 512  --osl 64    --batchSize 1

orin nx 16G 性能基准

测试模型

Qwen3-VL-2B-Instruct

  • 1-512-64: visual: 441(672x672), llm: 71
  • 1-1024-128: visual: 900(960x960), llm: 124

Qwen2-1.5B-Instruct

  • Qwen2-1.5B-Instruct
  • Qwen2-1.5B-Instruct-AWQ

Qwen2.5-0.5B-Instruct

  • Qwen2.5-0.5B-Instruct
  • Qwen2.5-0.5B-Instruct-AWQ
ModelBS-InSeq-OutSeqPrecisionOverall Latency (ms)Overall Token/s

Visual 

Encode Latency (ms)

LLM 

Prefill Latency (ms)

LLM 

Prefill Token/s

Decode Latency (ms)Decode Token/s

平均

DDR

带宽

(GB/s)

峰值

DDR

带宽

(GB/s)

理论计算带宽 (GB/s)
Qwen3-VL-2B-Instruct1-512-64

INT8

(15W)

Qwen3-VL-2B-Instruct1-512-64

INT8

(MAXN)

Qwen3-VL-2B-Instruct1-1024-128

INT8

(15W)

Qwen3-VL-2B-Instruct1-1024-128

INT8

(MAXN)

Qwen2-1.5B-Instruct1-512-64

INT8

(MAXN)

Qwen2-1.5B-Instruct-AWQ1-512-64

INT4

(MAXN)

Qwen2-1.5B-Instruct1-1024-128

INT8

(MAXN)

Qwen2-1.5B-Instruct-AWQ1-1024-128

INT4

(MAXN)

Qwen2.5-0.5B-Instruct1-512-64

INT8

(MAXN)

Qwen2.5-0.5B-Instruct-AWQ1-512-64

INT4

(MAXN)

Qwen2.5-0.5B-Instruct1-1024-128

INT8

(MAXN)

Qwen2.5-0.5B-Instruct-AWQ1-1024-128

INT4

(MAXN)

测试模型

Qwen3-VL-2B-Instruct

  • 1-512-55: visual: 196(448x448), llm: 316
模型量化类型序列长度KV CacheBPU 核数ViT(ms)TTFT(ms)Prefill TPSDecode TPS内存 (GB)
Qwen3-VL-2B-InstructW8A85121024

指标含义

指标计算方式涵盖阶段物理含义
Overall Token/s输出Token数 / 总体延迟Prefill + Decode总输出 Token 数 除以 端到端总延迟
Decode Token/s输出Token数 / Decode阶段耗时Decode 阶段仅衡量“生成速度”,更接近用户感知的“说话速度”

Token分配

常见图像尺寸对应的 qwen3-vl 视觉 token 数

尺寸 (H×W)是否能被 32 整除视觉 Token 数适用场景
224×224✅49轻量快速,适合小图
448×448✅196均衡推荐
576×576✅324接近 560 的替代
672×672✅441高清图,细节丰富
896×896✅784高分辨率,细粒度识别
1024×1024✅1024极限分辨率(需注意总 token 数)

DDR Bandwidth 计算

计算公式

  • 带宽 = 内存频率 (MHz) × 2 × 位宽 (Bits) ÷ 8 (把Bit变成Byte) ÷ 1000 (把MHz变成GB/s).
  • 位宽:Orin NX 的内存控制器(EMC)是 128-bit 的接口。
  • DDR 内存在一个周期内传输两次数据(上升沿和下降沿各一次),因此有效数据率是内存实际频率的两倍。

利用 tegrastats 监控 模型推理时的实时内存和显存带宽占用情况(例如 EMC_FREQ 或GR3D_FREQ 的变化),计算带宽峰值速率和均值速率。

  • GR3D_FREQ (GPU 频率与利用率): 这代表板载GPU(即iGPU)的运行状态。GR3D_FREQ X%@[Y, Y,...] 可以拆解为两部分来看:

    • X%:表示在当前频率下,GPU核心的实时利用率。这个数值越高,说明GPU计算越繁忙。

    • [Y, Y,...]:表示此时GPU各核心(在部分较新Jetson机型上会显示多个核心频率)的实际运行频率,单位是MHz。例如在AGX Orin等新平台上,你会看到类似 GR3D_FREQ 40%@[600, 600] 的输出,代表两个GPC核心都以600MHz运行,利用率为40%。

  • EMC_FREQ (外部内存控制器频率与带宽占用): 它反映的是内存控制器的繁忙程度,可以理解为内存带宽的“使用率”X%,后面通常跟着当前运行频率Y MHz。这个数值高,意味着CPU或GPU正在大量进行数据读写,内存带宽是此时系统的性能瓶颈之一。

已打包好两个脚本在 /home/pub/TensorRT-Edge-LLM/vlm_test

conda activate TensorRTLLM

cd ~/TensorRT-Edge-LLM
sh vlm_test/ddr.sh

监控脚本 ddr.sh

#!/bin/bash

export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM
export PYTHONPATH=/home/pub/TensorRT-Edge-LLM:$PYTHONPATH

# ================= 直接在这里赋值 =================
#MODEL_NAME="Qwen3-VL-2B-Instruct"    # 模型名称
#MODEL_NAME="Qwen2.5-0.5B-Instruct"
#MODEL_NAME="Qwen2.5-0.5B-Instruct-AWQ"
#MODEL_NAME="Qwen2-1.5B-Instruct"
MODEL_NAME="Qwen2-1.5B-Instruct-AWQ"

INPUT_TOKENS=512                      # Token 数量
#INPUT_TOKENS=1024
# ================================================

# ================= 配置区 =================
LOG_FILE="$MODEL_NAME/tegra_monitor_${INPUT_TOKENS}.log"
INTERVAL_MS=50 
# ==========================================

# 创建模型目录
mkdir -p "$MODEL_NAME"

echo "=========================================="
echo "DDR Bandwidth Monitoring & Analysis"
echo "=========================================="
echo "Model Name: $MODEL_NAME"
echo "Input Tokens: $INPUT_TOKENS"
echo "Log File: $LOG_FILE"
echo "Interval: ${INTERVAL_MS}ms"
echo "=========================================="

# 1. 清理历史残存进程
sudo pkill -f "tegrastats --interval" 2>/dev/null

# 2. 记录开始时间
START_TIME=$(date +%s%3N)  
echo "开始推理时间戳: $START_TIME"

# 3. 启动 tegrastats 后台记录
sudo tegrastats --interval $INTERVAL_MS > $LOG_FILE 2>/dev/null &
TEGRA_PID=$!
echo "tegrastats 已启动 (PID: $TEGRA_PID),日志写入: $LOG_FILE"

# 4. 执行 Qwen3-VL 推理程序
echo "正在执行模型推理..."
./build/examples/llm/llm_inference \
  --engineDir "$WORKSPACE_DIR/$MODEL_NAME/engines/llm" \
  --inputFile "$WORKSPACE_DIR/vlm_test/$MODEL_NAME/input_vlm_${INPUT_TOKENS}.json" \
  --outputFile "$WORKSPACE_DIR/vlm_test/$MODEL_NAME/output_vlm_${INPUT_TOKENS}.json"


RESULT_CODE=$?

# 5. 记录结束时间
END_TIME=$(date +%s%3N)
echo "结束推理时间戳: $END_TIME"

# 6. 杀掉 tegrastats 进程
sudo kill -SIGINT $TEGRA_PID
sleep 1
if ps -p $TEGRA_PID > /dev/null; then
    sudo kill -9 $TEGRA_PID 2>/dev/null
fi

echo "监控结束。总耗时: $((END_TIME - START_TIME)) 毫秒。"
echo ""

# 7. 计算带宽 
echo "=========================================="
echo "开始分析 DDR 带宽..."
echo "=========================================="

# Python 脚本路径
PYTHON_SCRIPT="/home/pub/TensorRT-Edge-LLM/vlm_test/tegra_monitor.py"

# 检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; then
    echo "❌ 错误: 日志文件不存在: $LOG_FILE"
    exit 1
fi

# 检查 Python 脚本是否存在
if [ ! -f "$PYTHON_SCRIPT" ]; then
    echo "❌ 错误: Python 脚本不存在: $PYTHON_SCRIPT"
    exit 1
fi

# 调用 Python 脚本,传递 MODEL_NAME 和 INPUT_TOKENS
echo "执行命令: python3 $PYTHON_SCRIPT $MODEL_NAME $INPUT_TOKENS"
python3 "$PYTHON_SCRIPT" "$MODEL_NAME" "$INPUT_TOKENS"

# 检查执行结果
if [ $? -eq 0 ]; then
    echo ""
    echo "✅ 带宽分析完成!"
    echo "📄 结果已保存到: $MODEL_NAME/bandwidth_result_${INPUT_TOKENS}.txt"
else
    echo ""
    echo "❌ 带宽分析失败,请检查错误信息"
    exit 1
fi

echo "=========================================="
echo "全部完成!"
echo "=========================================="

# 8.确保完全释放终端 
stty echo 2>/dev/null
exit $RESULT_CODE

带宽计算脚本  tegra_monitor.py

#!/usr/bin/env python3
import re
import os
import sys

# ================== 从命令行读取参数 ==================
def print_usage():
    print("用法: python3 tegra_monitor.py <MODEL_NAME> <INPUT_TOKENS>")
    print("示例:")
    print("  python3 tegra_monitor.py Qwen3-VL-2B-Instruct 512")
    print("  python3 tegra_monitor.py Llama-2-7B 1024")
    sys.exit(1)

if len(sys.argv) < 3:
    print("错误: 缺少参数")
    print_usage()

# 读取参数
MODEL_NAME = sys.argv[1]
INPUT_TOKENS = int(sys.argv[2])

# ================== 构建文件路径 ==================
# 构建输入日志文件名
log_file = f"{MODEL_NAME}/tegra_monitor_{INPUT_TOKENS}.log"

# 构建输出文件名
output_filename = f"bandwidth_result_{INPUT_TOKENS}.txt"
OUTPUT_FILE = f"{MODEL_NAME}/{output_filename}"

# 确保目录存在
os.makedirs(MODEL_NAME, exist_ok=True)

def log_print(*args, **kwargs):
    """既打印到屏幕,又写入文件的函数"""
    print(*args, **kwargs)
    content = " ".join(str(arg) for arg in args)
    with open(OUTPUT_FILE, 'a', encoding='utf-8') as f:
        f.write(content + "\n")

# 清空输出文件
with open(OUTPUT_FILE, 'w') as f:
    f.write(f"# DDR Bandwidth Analysis Result\n")
    f.write(f"# Model: {MODEL_NAME}\n")
    f.write(f"# Log file: {log_file}\n")
    f.write(f"# Input Tokens: {INPUT_TOKENS}\n")
    f.write(f"# Generated at: {os.popen('date').read().strip()}\n")
    f.write(f"# {'='*50}\n\n")

# ================== 主要处理逻辑 ==================
emc_freqs = []

try:
    with open(log_file, 'r') as f:
        lines = f.readlines()
except FileNotFoundError:
    log_print(f"错误:日志文件不存在 - {log_file}")
    log_print("请先运行 tegrastats 监控生成日志文件")
    sys.exit(1)

for line in lines:
    match = re.search(r'EMC_FREQ\s+\d+%@(\d+)', line)
    if match:
        freq_mhz = int(match.group(1))
        emc_freqs.append(freq_mhz)

# 带宽 = 内存频率 (MHz) × 2 × 位宽 (Bits) ÷ 8 (把Bit变成Byte) ÷ 1000 (把MHz变成GB/s).
# 128-bit ddr 128/8=16
if emc_freqs:
    peak_freq = max(emc_freqs)
    avg_freq = sum(emc_freqs) / len(emc_freqs)
    
    peak_bw = peak_freq *2 * 16 / 1000
    avg_bw = avg_freq *2 * 16 / 1000
    
    log_print(f"模型名称: {MODEL_NAME}")
    log_print(f"采样点数: {len(emc_freqs)} 个")
    log_print(f"EMC最高频率: {peak_freq} MHz")
    log_print(f"EMC平均频率: {avg_freq:.0f} MHz")
    log_print("-" * 30)
    log_print(f"【DDR 峰值带宽】: {peak_bw:.2f} GB/s")
    log_print(f"【DDR 平均带宽】: {avg_bw:.2f} GB/s")
    
    # 额外输出汇总信息到文件头部
    with open(OUTPUT_FILE, 'r+') as f:
        content = f.read()
        f.seek(0)
        summary = f"""# ========== Summary ==========
# Model: {MODEL_NAME}
# Total samples: {len(emc_freqs)}
# Peak frequency: {peak_freq} MHz
# Average frequency: {avg_freq:.0f} MHz
# Peak bandwidth: {peak_bw:.2f} GB/s
# Average bandwidth: {avg_bw:.2f} GB/s
# {'='*50}

"""
        f.write(summary + content)
else:
    log_print("未能从日志中解析出 EMC_FREQ,请检查 tegrastats 版本。")

模型的token计算

由于TensorRT-Edge-LLM的 有自己的输入模板,所以模板会占用一部分token。以下是针对纯文本和混合模型的输入token计算。

纯文本输入 llm 的 token 计算 check_llm_length.py

from transformers import AutoTokenizer

# 调用模型自身的分词器
MODEL_PATH = "/home/pub/TensorRT-Edge-LLM/Qwen/Qwen2.5-0.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)

# 用户输入文本
user_text = "用户文本"

# 标准对话消息
messages = [
                {"role": "user", "content":user_text}
]

# 原生模板渲染,和TensorRT-Edge-LLM内部逻辑完全一致
prompt = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True   # 开启:追加 <|im_start|>assistant\n
)

# 完整送入LLM的序列ids
input_ids = tokenizer.encode(prompt, add_special_tokens=False)
total_seq_len = len(input_ids)

# 单独统计用户文本自身token(仅作参考)
user_only_ids = tokenizer.encode(user_text, add_special_tokens=False)
user_only_len = len(user_only_ids)

print(f"==== Qwen2 纯文本Token统计 ====")
print(f"用户文本单独token数量:{user_only_len}")
print(f"【送入LLM Prefill总序列长度(框架校验值)】:{total_seq_len}")
print(f"差值 = 对话模板所有角色标记token总和")

视觉和文本混合输入的 llm token 计算 check_llm_length.py

import os
from transformers import AutoTokenizer

MODEL_PATH = "/home/pub/TensorRT-Edge-LLM/Qwen/Qwen3-VL-2B-Instruct"

# 1. 直接用 AutoTokenizer 加载本地分词器
try:
    tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
except Exception as e:
    print(f"❌ 加载失败: {e}")
    exit(1)
    

# 2. 你的纯文本内容 (从 JSON 的 "text" 字段提取)
user_text = """用户文本"""

user_text_tokens = tokenizer.encode(user_text, add_special_tokens=False)
user_text_tokens_len = len(user_text_tokens)


print("=" * 50)
print(f"✅ user_tex 输入 Token 数 = {user_text_tokens_len} 个")
print("=" * 50)



# 3. 图片产生的视觉 Token
image_width = 960 
image_height = 960

# Qwen3-VL 视觉参数:patch_size=16, merge_size=2
grid_h = (image_height) // 16
grid_w = (image_width) // 16
total_patches = grid_h * grid_w
# merge_size=2 表示 2x2 合并,Token 数除以 4
image_tokens = total_patches // (2 * 2) 

print(f"【 图像】产生的视觉 Token: {image_tokens} 个")



# 4. 查看模型输入框架,计算llm真实输入token数
# ============================
# 查看模型输入框架
# ============================
messages=[
                {
                    "role": "system",
                    "content": "You are a helpful assistant."
                },
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "image",
                            "image": "/home/pub/TensorRT-Edge-LLM/vlm_test/vlm_test_img/Cloud_woman_960x960.png"
                        },
                        {
                            "type": "text",
                            "text": user_text
}
]
}
]

# 获取框架原生模板文本
raw_template_str = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
print("====原生模板字符串====")
print(repr(raw_template_str))

# 把原生模板里【单个image_pad】替换成N个image_pad,模拟框架行为
real_str = raw_template_str.replace("<|image_pad|>", "<|image_pad|>"*image_tokens)
real_ids = tokenizer.encode(real_str, add_special_tokens=False)
print(f"\n【框架对齐版本】token总长度:{len(real_ids)}")

五、TesorRT-Edge-LLM v0.6.0 部署

支持 Qwne2_vl 模型的视觉分支onnx导出及模型转化

安装

Part 1: Quantization and tensorrt_edgellm (x86 Host with GPU / Edge Device)

Verify Your Prerequisites

# Check CUDA installation
nvcc --version
# Should show CUDA 12.x or 13.x

# Check GPU and available memory
nvidia-smi
# Look for GPU memory (e.g., "24576MiB" for 24GB)

# Check Python version
python3 --version
# Should show Python 3.10 or higher

下载资源库及依赖

创建好虚拟环境后,导入系统的tensorrt10.3,安装 torch-2.10.0-cp310-cp310-linux_aarch64.whl

# 安装源码
git clone -b v0.6.0 --single-branch https://github.com/NVIDIA/TensorRT-Edge-LLM
cd TensorRT-Edge-LLM
git submodule update --init --recursive

# 创建环境
conda create --name tensorrtllmv060 python=3.10.12
conda activate tensorrtllmv060

# 导入系统环境,系统tensorrt
export PYTHONPATH=/usr/lib/python3.10/dist-packages:$PYTHONPATH

# 安装 orin 适配的torch
pip install /home/pub/torch_for_jetpack6.2/torch-2.10.0-cp310-cp310-linux_aarch64.whl

# 安装依赖
pip3 install .

验证环境

# Test export tools
tensorrt-edgellm-export-llm --help
tensorrt-edgellm-quantize-llm --help

Part 2: C++ Runtime (Edge Device)

mkdir build
cd build

cmake ..     \
-DCMAKE_BUILD_TYPE=Release     \
-DTRT_PACKAGE_DIR=/usr     \
-DCMAKE_TOOLCHAIN_FILE=cmake/aarch64_linux_toolchain.cmake     \
-DEMBEDDED_TARGET=jetson-orin

LLM导出模型、量化模型(x86 / Jetson)

# 设置环境变量
export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM
export PYTHONPATH=$WORKSPACE_DIR:$PYTHONPATH
export MODEL_NAME=Qwen2-VL-2B

# 设置 hugging face 镜像链接
export HF_ENDPOINT=https://hf-mirror.com
pip install huggingface-hub==1.5.0

# Quantize language model
tensorrt-edgellm-quantize-llm \
  --model_dir Qwen/$MODEL_NAME \
  --quantization fp8 \
  --output_dir $MODEL_NAME/quantized

# Export language model
tensorrt-edgellm-export-llm \
  --model_dir $MODEL_NAME/quantized \
  --output_dir $MODEL_NAME/onnx/llm

# Export visual encoder
tensorrt-edgellm-export-visual \
  --model_dir Qwen/$MODEL_NAME \
  --output_dir $MODEL_NAME/onnx/visual

LLM转换模型(Jetson)

# 设置环境变量
export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM
export PYTHONPATH=$WORKSPACE_DIR:$PYTHONPATH
export MODEL_NAME=Qwen2-VL-2B


# Build language model engine
./build/examples/llm/llm_build \
  --onnxDir $WORKSPACE_DIR/$MODEL_NAME/onnx/llm \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm \
  --maxBatchSize 1 \
  --maxInputLen 1024 \
  --maxKVCacheCapacity 4096

# Build visual encoder engine
./build/examples/multimodal/visual_build \
  --onnxDir $WORKSPACE_DIR/$MODEL_NAME/onnx/visual \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual \
  --minImageTokens 128 \
  --maxImageTokens 512 \
  --maxImageTokensPerImage 512

量化精度

orin 在 v0.6.0版本 只支持 int4_awq

Inputs

Outputs

Description

quantize-llm

HuggingFace Model

Quantized Model

Quantize LLM models using NVIDIA ModelOpt. Supports FP8, INT4 AWQ, and NVFP4 quantization methods for memory reduction and performance optimization

export-llm

HuggingFace/Quantized Model

ONNX Model

Export LLM models to ONNX format. Handles standard LLMs and EAGLE base models with precision-specific optimizations and graph surgery

export-visual

VLM Model

Visual ONNX

Export visual encoders for multimodal models. Supports vision components with FP8 quantization and dynamic resolution

export-draft

Base + Draft Models

Draft ONNX

Export EAGLE draft models for speculative decoding. Specialized export for EAGLE3 draft model architectures with vocabulary mapping support

quantize-draft

Base + Draft Models

Quantized Draft

Quantize EAGLE draft models. Specialized quantization for draft models using base model inputs for calibration

insert-lora

ONNX Model

LoRA-enabled ONNX

Insert LoRA patterns into existing ONNX models. Adds dynamic LoRA support to ONNX models by modifying the computational graph

process-lora

LoRA Weights

SafeTensors

Process LoRA weights for runtime use. Processes LoRA adapter weights according to TensorRT Edge-LLM specifications for runtime loading

LLM模型推理(Jetson)

创建一个输入文件 $WORKSPACE_DIR/input_vlm.json,替换图片路径

{
    "batch_size": 1,
    "temperature": 1.0,
    "top_p": 1.0,
    "top_k": 50,
    "max_generate_length": 128,
    "requests": [
        {
            "messages": [
                {
                    "role": "system",
                    "content": "You are a helpful assistant."
                },
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "image",
                            "image": "/path/to/image.jpg"
                        },
                        {
                            "type": "text",
                            "text": "Please describe the image."
                        }
                    ]
                }
            ]
        }
    ]
}

开始推理

# 设置环境变量
export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM
export PYTHONPATH=$WORKSPACE_DIR:$PYTHONPATH
export MODEL_NAME=Qwen2-VL-2B

./build/examples/llm/llm_inference \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm \
  --multimodalEngineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual \
  --inputFile $WORKSPACE_DIR/input_vlm.json \
  --outputFile $WORKSPACE_DIR/output_vlm.json

性能基准

llm性能测试

依旧可以通过llm_bench测试模型性能,但只支持llm。看参数命令,相当于运行了一遍trtexec进行模型推理。

visual 性能测试

视觉分支模型直接使用trtexec进行推理。

# 首先需要导入tensorrt-edge-llm的插件依赖,否则trtexec会找不到依赖
export LD_PRELOAD=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

# 开始推理
trtexec \
--loadEngine=xxx.engine  \
--iterations=1000 \
--duration=0 \
--warmUp=1000  \
--useCudaGraph \
--noDataTransfers \
--shapes=

qwen2_vl视觉分支测试实践

tensorrt-edge-llm v0.6.0的版本暂不支持huggingface上已量化好的模型,也不支持视觉模块量化,且官方统计的visual encoder性能是未量化的。

模型导出

export WORKSPACE_DIR=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM
export PYTHONPATH=$WORKSPACE_DIR:$PYTHONPATH
export MODEL_NAME=Qwen2-VL-2B

tensorrt-edgellm-export-visual   \
--model_dir Qwen/$MODEL_NAME   \
--output_dir $MODEL_NAME/onnx/visual

模型转换

./build/examples/multimodal/visual_build   \
--onnxDir $WORKSPACE_DIR/$MODEL_NAME/onnx/visual   \
--engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual   \
--minImageTokens 128   \
--maxImageTokens 1024   \
--maxImageTokensPerImage 1024

查看引擎基础信息

polygraphy inspect model  /home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM/Qwen2-VL-2B/engines/visual/visual/visual.engine  --mode=engine

发现模型的最大最小输入的shape是设置的token大小的4倍,这是因为:

  • --maxImageTokens是下采样后的最终视觉 token 数(输入给 LLM 的)。
  • 引擎输入input(hw,1176)是下采样前的 Patch token 数(ViT 主干的实际输入)。
  • qwen2_vl的视觉部分做了2D 下采样(空间分辨率减半,H/2 × W/2) 最终视觉 tokens = Patch tokens / 4

模型性能

设置需要输入到llm的ImageToken=900

trtexec \
--loadEngine=/home/pub/TensorRT-Edge-LLM_v0.6.0/TensorRT-Edge-LLM/Qwen2-VL-2B/engines/visual/visual/visual.engine  \
--iterations=1000 \
--duration=0 \
--warmUp=1000  \
--useCudaGraph \
--noDataTransfers \
-shapes=input:3600x1176,rotary_pos_emb:3600x40,cu_seqlens:2,max_seqlen_carrier:3600

六、NVIDIA Nsight Systems

nsys需要在sudo权限下才能收集更全面的信息

nsys 分析 llm_inference的完整执行过程

# 命令一共是两部分、nsys监测 + 运行你原本的脚本

sudo nsys profile  --trace=cuda,nvtx,osrt \
    --output=llm_inference_profile \
    --force-overwrite=true \
    ./build/examples/llm/llm_inference \
  --engineDir $WORKSPACE_DIR/$MODEL_NAME/engines/llm \
  --multimodalEngineDir $WORKSPACE_DIR/$MODEL_NAME/engines/visual \
  --inputFile $WORKSPACE_DIR/vlm_test/input_vlm.json \
  --outputFile $WORKSPACE_DIR/vlm_test/output_vlm.json

# sys 监测部分
sudo nsys profile  --trace=cuda,nvtx,osrt \
    --output=llm_inference_profile \
    --force-overwrite=true

命令解析

--trace=cuda,nvtx,osrt:追踪 CUDA API、NVTX 标记(如果有)和操作系统运行时活动。

--output=llm_inference_profile:指定输出文件的前缀名。

--force-overwrite=true:若输出文件已存在则直接覆盖。

其余部分是 llm_inference 程序及其参数

nsys 分析 trtexec 的完整执行过程

# 如果是 TensorRT-Edge-LLM 生成的 onnx 模型转的模型 会报 Cannot find plugin 的错误
export LD_PRELOAD=/home/pub/TensorRT-Edge-LLM/build/libNvInfer_edgellm_plugin.so

# nsys 生成分析数据
sudo nsys profile -o /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/profile \
            --capture-range=cudaProfilerApi \
            --cuda-graph-trace=node \
            trtexec \
            --loadEngine=/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/llm/model.onnx.engine  \
            --warmUp=0 \
            --duration=0 \
            --iterations=50

# 这样也可以
sudo nsys profile -o /home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/profile \
--capture-range=cudaProfilerApi \
--cuda-graph-trace=node \
trtexec \
--loadEngine=/home/pub/TensorRT-Edge-LLM/Qwen3-VL-2B-Instruct/oss_engine/tokens1000/visual/model.onnx.engine \
--warmUp=0 \
--duration=0 \
--iterations=50

生成 Nsight System GUI可以直接打开的.nsys-rep数据

只生成 .qdstrm 文件而没有自动转为 .nsys-rep,通常是因为 nsys 找不到或无法运行它的格式转换工具 QdstrmImporter。 .qdstrm 是一个中间格式的临时文件。

nsys profile 正常结束时会调用 QdstrmImporter 将 .qdstrm 转换为最终的 .nsys-rep 报告。如果转换失败,就会只剩下 .qdstrm 文件。

#  QdstrmImporter
find / -name QdstrmImporter 2>/dev/null

# QdstrmImporter 在.qdstrm 文件同目录下生成一个同名的 .nsys-rep 文件
/path/to/QdstrmImporter -i /path/to/your/profile.qdstrm

七、ORIN NX 配置交换空间

建议配置高质量的 NVMe SSD 作为 Swap 分区,以防内存溢出导致进程被杀。

orin nx 设备上已经设置了近 8G 的 swap 分区,但无法支持大模型。

检查当前swap状态

swapon --show          # 查看激活的swap设备
free -h                # 查看内存和swap总大小

显示Jetson Orin 当前使用的是 ZRAM(压缩内存交换),而不是磁盘上的交换文件。

ZRAM 的原理:它从你的物理内存(15GB)中划出一部分空间,经过压缩后当作交换分区使用。由于压缩比通常可达 2~3 倍,所以它提供了 7.6GB 的交换空间,但实际只占用物理内存约 2~3GB。

优势与局限:ZRAM 速度极快(因为还是在内存里读写),但它无法无限扩大,因为它本质上是“借用”物理内存。你看到的 Swap 7.6G,实际上已经是当前配置的容量上限了。

创建磁盘交换文件

将文件放在 SSD 或 NVMe 。

# 创建8GB大小的交换文件
sudo fallocate -l 16G /swapfile
# 设置文件权限(仅root可读写,保障安全)
sudo chmod 600 /swapfile
# 格式化为Swap分区
sudo mkswap /swapfile
# 立即启用交换空间
sudo swapon /swapfile

八、临时退出图形界面

  1. 按 Ctrl + Alt + F3(或F4、F5等)。这会切换到另一个纯文本的控制台(tty)。你需要用你的用户名和密码重新登录一次。
  2. 登录后,执行命令关闭图形桌面服务,需要再输入一次密码。
    sudo systemctl stop gdm3
  3. 查看gdm3状态

    systemctl status gdm3
  4. jtop 查看当前内存占用情况

    jtop
  5. 返回图形界面

    sudo systemctl start gdm3
  6. 然后按 Ctrl + Alt + F1(或F2,取决于你的系统配置)通常就可以切换回图形登录界面。

九、功耗 & 性能模式锁满算力

测试模型最佳性能需要开启满功耗模式(MAXN)和 GPU 频率锁定(因为ORIN会动态调频)

1.开启MAXN(命令或者手动选择Power mode)

sudo nvpmodel -m 0

2.锁定CPU/GPU/DLA/PVA全部最高频率

sudo jetson_clocks

3.查看当前 GPU 频率

sudo jetson_clocks --show

开发板默认是DVFS(Dynamic voltage and frequency scaling (DVFS) is the adjustment of power and speed settings on a computing device’s various processors)模式,

长时间满载会触发热节流,温度达到上限则强制降频。如果需要在某个程序运行时使用高性能模式,程序运行结束后调回DVFS模式,请跟随:

1.保存当前的DVFS模式

sudo jetson_clocks --store

2.开启锁频,程序执行结束后,恢复DVFS模式

sudo jetson_clocks --restore

十、Qwen3-2B-VL-Instruct 导出模型维度一览

vision Input

张量名称原动态维度500 tokens 对应 shape修改逻辑
input[total_tokens, 1536][500, 1536]第一维是视觉 token 总数,为核心修改项;隐层维度 1536 固定不变
rotary_pos_emb[total_tokens, 32][500, 32]旋转位置编码张量,第一维必须和输入 token 数严格对齐;编码维度 32 固定
cu_seqlens[batch_p1][2]累积序列长度张量,形状仅和 batch 数相关(单 batch = batch+1 = 2),形状本身不随 token 数变化;张量内部的数值需要同步设为 [0, 500],和输入长度匹配
max_seqlen_carrier[max_seqlen][500]最大序列长度载体,维度值等于支持的最大视觉 token 数,需和输入长度对齐
fast_pos_embed_idx[4, total_tokens][4, 500]快速位置嵌入索引,第一维组数 4 固定,第二维对齐 token 总数
fast_pos_embed_weight[4, total_tokens][4, 500]快速位置嵌入权重,和索引张量形状一一对应,第二维同步修改

参考输入

静态

shapes=input:500x1536,rotary_pos_emb:500x32,cu_seqlens:2,max_seqlen_carrier:500,fast_pos_embed_idx:4x500,fast_pos_embed_weight:4x500

动态

minShapes=input:1x1536,rotary_pos_emb:1x32,cu_seqlens:2,max_seqlen_carrier:1,fast_pos_embed_idx:4x1,fast_pos_embed_weight:4x1 \
optShapes=input:500x1536,rotary_pos_emb:500x32,cu_seqlens:2,max_seqlen_carrier:500,fast_pos_embed_idx:4x500,fast_pos_embed_weight:4x500 \
maxShapes=input:1000x1536,rotary_pos_emb:1000x32,cu_seqlens:2,max_seqlen_carrier:1000,fast_pos_embed_idx:4x1000,fast_pos_embed_weight:4x1000

llm Input

更多推荐