Jetson TensorRT vs RK3588 RKNN:两块板子都跑通了 LLM 和 VLM 之后,我悟了
先交代背景:我手头有两块边缘设备,一块 NVIDIA Jetson Orin(8GB,GPU 路线),一块 Orange Pi 5 Plus(RK3588,16GB,NPU 路线)。过去一段时间我把 LLM 和 VLM 在两块板上各部署了一遍------Jetson 上用过 TensorRT、TensorRT-Edge-LLM、MLC-LLM、llama.cpp,RK3588 上用 rknn-llm 官方工具链跑通了文本和多模态。全部真实数据,全部亲自踩坑。
折腾完回头一看,发现一个特别有意思的事:这两套看起来八竿子打不着的工具链,骨子里是同一套范式。今天就把两边的部署过程摊开对比,聊聊哪里像、哪里不像、选型该怎么选。
🧰 两套工具链长啥样
先把两边的"全家福"摆出来:
| Jetson Orin(NVIDIA) | Orange Pi 5 Plus(Rockchip RK3588) | |
|---|---|---|
| 算力单元 | GPU(sm_87,统一内存 8GB) | NPU 三核(各 2 TOPS,共 6 TOPS)+ CPU |
| CV 推理引擎 | TensorRT 10.3.0 | rknn-toolkit2 2.3.2 + librknnrt |
| LLM 工具链 | TensorRT-Edge-LLM 0.6.0 / MLC-LLM 0.20 | rkllm-toolkit 1.3.0 + librkllmrt |
| VLM 方案 | llama.cpp(GGUF) | rknn-llm 多模态 demo(视觉 rknn + 语言 rkllm) |
| 模型格式 | .engine / MLC 的 -q4f16_1 / GGUF |
.rknn / .rkllm |
| 板端系统 | JetPack 6.x(L4T R36.x,CUDA 12.6) | Armbian 26.8.2(vendor 内核 6.1.115) |
一句话概括:NVIDIA 是"一套 CUDA 打天下",Rockchip 是"视觉一套工具、语言一套工具,各干各的"。这个差异贯穿后面所有环节。
🔄 CV 模型:两边居然是同一个模子
先从最简单的 ResNet18 说起。Jetson 上的流程:
PyTorch → torch.onnx.export → trtexec --onnx --saveEngine --fp16 → .engine → 板端 TensorRT API 跑
RK3588 上跑 Qwen3-VL 的视觉塔,流程是:
PyTorch → torch.onnx.export → rknn-toolkit2 build → .rknn → 板端 librknnrt 跑
看出来没?一模一样。都是"框架图 → ONNX 公证层 → 厂商编译器 → 设备绑定二进制 → 板端专用 runtime"。
为什么都绕不开 ONNX?因为 NPU/GPU 编译器吃不了"活的" PyTorch 代码。HuggingFace 模型本质是动态 Python 程序,编译器需要一张静态、封闭的计算图。所以第一步必须 trace 冻图,第二步编译成目标芯片的指令流。我之前专门琢磨过这个问题:rknn-toolkit2 其实有 load_pytorch 接口,但它要求传入的也是 traced model------冻图这一步怎么都省不掉,ONNX 只是把"冻图"和"编译"解耦了。
拆两步还有个工程上的好处:出错能二分 。我在 RK3588 上转换视觉塔时,ONNX 导出 20 秒就验完了输出形状([49, 2048]),后面 ONNX→RKNN 首跑报 pkg_resources 缺失,我立刻就知道是编译段的环境问题(setuptools 84 移除了老 API,降级解决),跟导出没关系。要是一条道跑到黑,这种问题就得两头猜。
相同点还不止这些:
- 产物都绑芯片型号 :
.engine绑 sm_87,.rknn绑 rk3588,换芯片都得重编 - 产物都要 md5 校验后推板:两边我都推过 G 级文件,G 级文件不查 md5 就是给自己埋雷
- 都有算子兜底机制 :TensorRT 不支持的算子写 plugin;RKNN 不支持的落到 CPU(
op_target指定) - 量化思想一致:离线用代表性数据统计分布,换低精度加速。TensorRT INT8 要校准集,RKNN 的 W8A8 同样要校准数据
🧠 LLM 部署:Jetson 三条路,RK3588 一条路
Jetson 上我玩过三种姿势
| 路线 | 思路 | 我的实测 |
|---|---|---|
| TensorRT-Edge-LLM 0.5B | runtime 优化,engine + plugin | Qwen2.5-0.5B,OpenAI 兼容 API 好接,但 v0.6.0不支持流式输出 |
| MLC-LLM 1.5B/3B | 编译器路线,PC 交叉编译 | 1.5B 60 tok/s、TTFT 0.077s,是目前 Jetson 上最快的一档 |
| llama.cpp | GGUF 直接吃 | 省事,兼容性最好,VLM 靠它 |
Jetson 最大的坑是 8GB 内存 :MLC-LLM 3B 必须 MAX_SEQ_LEN=2048(从 4096 砍半)才塞得下;JIT 编译前必须开 8GB swap 防 OOM;后来跑 VLM 时还发现两套 CUDA 服务不能并存,测 Qwen3-VL 前得先把常驻的 Qwen2.5-VL 服务 kill 掉腾内存。大模型编译这种重活,我干脆放 PC 上交叉编译好再推板。
RK3588 上 rkllm 一条道
RK3588 的官方路线 rknn-llm 是"PC 端转换期量化":用 rkllm-toolkit 把 HF 权重直接转成 .rkllm。我转 Qwen3-VL-2B 的 W8A8 用了 33 分钟(其中 28 层逐层优化占 22 分钟,48 秒/层,全程最慢段),产物 2.3G。
转换前要先生成校准数据,这一步文本和多模态不一样:
- 文本路线:纯文本 prompt 进,prompt 出
- 多模态路线 :跑 20 条 MMBench 图文样本的前向,hook 截获语言模型入口的
inputs_embeds------校准数据是图文联合嵌入,不是纯文本。这个细节挺讲究的,量化分布得跟实际推理时的分布对上
实测性能(端侧 HTTP 口径):1.5B decode 23.8 tok/s,3B 13.5 tok/s,TTFT(256 字提示)541/919 ms。跟 Jetson MLC 的 60 tok/s 比,纯文本速度确实差一截,但两块板子价钱差得更多。
LLM 踩坑风格对比
两边坑的类型完全不同,挺能说明工具链成熟度:
- Jetson 的坑是"资源型":内存不够、swap、并发服务互踩------芯片太强但板子内存太小,全是物理约束
- RK3588 的坑是"工程型" :NPU 驱动 0.9.6 跑 rknn 静默输出乱码(差 0.0.2 版本!)、官方脚本内部命名不一致(生成写
llm_inputs.json、导出读inputs.json)、--savepath参数目录部分被忽略......都是版本对齐和细节规范问题 。Rockchip 工具链迭代快,但粗糙的地方不少,用之前先读仓库源码,别全信博客------我踩的 5 个坑全是参考博客(老版本)和实操(新版本)行为差异导致的
👁️ VLM 部署:两边哲学完全不同
这是我觉得最有意思的部分。同样是 Qwen3-VL-2B,两边走了两条截然不同的路。
Jetson:llama.cpp 一把梭
GGUF 格式直接吃,一条命令起服务:
bash
llama-server -m Qwen3VL-2B-Instruct-Q8_0.gguf --mmproj mmproj-F16.gguf \
-ngl 99 --ctx-size 3072 --port 8081
视觉和语言都在一个模型文件体系里(mmproj 单独放视觉投影层),动态分辨率------图多大切多少 patch,一张图约 604 个视觉 token,看图细节丰富。4.4 秒加载完事,OpenAI 兼容 API 直接用。整个过程没有任何"转换"环节,下载即部署,对用户极其友好。
RK3588:外科手术式拆分
rknn-llm 的思路是把 VLM 拆成"眼睛"和"大脑",两条工具链分别伺候:
Qwen3-VL-2B(一个 HF 仓库)
├── 视觉塔:PyTorch → ONNX → .rknn(rknn-toolkit2,fp32 不量化,795M)
└── LLM: PyTorch → .rkllm(rkllm-toolkit,W8A8 量化,2.3G)
板端:图片 → imgenc(.rknn) → 图像特征[49,2048]
→ 替换 prompt 里的 <|image_pad|> 占位 → demo(.rkllm) → 文本
因为分辨率被钉死在 224×224(448 会超 NPU 寄存器限制,转换成功运行也崩),每张图固定 49 个视觉 token ------是 Jetson 的零头。官方 demo 只有前台交互程序,没有 HTTP 服务,我还得自己基于 ctypes 封装写了个 OpenAI 兼容的 Flask 服务。另外还有个隐蔽的坑:交互输入必须包含 <image> 字样,否则走纯文本通道,图像嵌入压根不注入,模型会一本正经地回答"我无法查看图片"。
一个工具链成熟度的直观对比:Jetson 上 VLM 从下载到服务跑起来 6 分钟;RK3588 上同样的事,PC 端两套 venv、两次转换共 40 分钟、demo 编译、自研 HTTP 层,以天计。llama.cpp 是"傻瓜相机",rknn-llm 是"手动挡"------前者省心,后者对 NPU 的利用更彻底(毕竟 llama.cpp 在 RK3588 上官方版只走 CPU,NPU 要靠社区 fork,而社区 fork 又有驱动版本坑)。
⚔️ 正面交锋:同模型同精度对打
光说不练假把式。我做了个能回答"RK3588 到底慢多少"的实验:同模型(Qwen3-VL-2B)、同 8bit 精度档(W8A8 vs Q8_0)、同图同问同测试脚本,唯一变量是硬件。
| 指标(中位) | RK3588 · 3×NPU | Jetson Orin · GPU | 倍数 |
|---|---|---|---|
| TTFT(图已缓存,热态) | 164.2 ms | 165.8 ms | ≈1.0×,打平 |
| decode 输出速度 | 12.74 tok/s | 31.29 tok/s | 2.46× |
| 单轮总耗时(出 256 token) | 19.8 s | 8.3 s | 2.4× |
| 每图视觉 token | 49(固定) | ~604(动态) | 前端根本不同 |
两个发现特别值得说:
① TTFT 为什么能打平? 因为两边都吃了缓存红利(图特征进 prompt cache),首包延迟由小段增量 prefill 和网络开销主导,这部分两边半斤八两。用户感知的"反应快不快",两块板是一个水平。
② decode 差 2.5 倍,物理上完全自洽。 decode 是纯内存带宽瓶颈:RK3588 算下来 12.74 tok/s × 2.3G ≈ 29 GB/s,而 LPDDR4x 理论带宽 31.6 GB/s------利用率 92%,已经打满了;Jetson 侧 57 GB/s。带宽决定天花板,这不是优化能救的,是硬件代差。
还有一个容易被忽略的点:输入信息量天然不对等。Jetson 每图 604 token,看图细节明显更细腻;RK3588 的 49 token 是拿"看得粗"换"每图成本低"。如果你做质量盲评,得知道两边根本没看同一份"图"。
📊 总结:像的部分和不像的部分
像的部分(范式同构):
- 都是"离线编译 + 板端 runtime"两段式
- 都以 ONNX 为中间公证层(LLM 权重打包除外)
- 产物都绑死芯片型号、都要 md5 推板校验
- 都有量化体系(INT8/W8A8)和算子兜底(plugin/CPU fallback)
- LLM 侧殊途同归:权重离线量化打包 + 专用 runtime 逐 token 生成 + OpenAI 兼容 HTTP 包装
不像的部分:
| 维度 | TensorRT(Jetson) | RKNN/RKLLM(RK3588) |
|---|---|---|
| 动态 shape | 原生支持(optimization profile) | 基本静态,转换时钉死 |
| VLM 分辨率 | 动态,细节丰富 | 固定 224,省成本 |
| 工具链数量 | CV/LLM/VLM 生态各自成熟,llama.cpp 兜底 | 视觉、语言两套工具链,各自独立版本 |
| 踩坑类型 | 资源型(内存/swap) | 工程型(驱动版本/命名不一致) |
| 部署体验 | 下载即用(llama.cpp)或交叉编译(MLC) | PC 转换链路长,板端常要自己补服务层 |
| decode 天花板 | 带宽 57 GB/s | 带宽 29 GB/s(已跑满 92%) |
选型建议(个人向):
- 要看图质量(OCR、细节描述、grounding):Jetson 动态分辨率优势太大,没得选
- 要成本 sensitive 的纯文本对话:RK3588 够用,TTFT 同级,decode 慢 2.5 倍但价格是零头
- 要快速验证/原型:Jetson + llama.cpp,零转换成本
- 要深度榨干国产 NPU:RK3588 + rknn-llm,做好读源码的心理准备
- CV 小模型:两边一个套路,TensorRT FP16 和 RKNN fp16/fp32 随便选,TensorRT 生态文档更成熟
环境版本锚定:Jetson Orin 8GB / JetPack 6.x / CUDA 12.6 / TensorRT 10.3.0 / TensorRT-Edge-LLM 0.6.0 / MLC-LLM 0.20.0 / llama.cpp b10618;Orange Pi 5 Plus 16GB / Armbian 26.8.2 Trixie / vendor 内核 6.1.115 / RKNPU 驱动 v0.9.8 / rknn-toolkit2 2.3.2 / rkllm-toolkit 1.3.0。对比协议:同模型 Qwen3-VL-2B、同 8bit 精度档(W8A8 vs Q8_0)、同图同问同 HTTP 测试脚本、3 问 × 3 轮取中位、max_tokens=256、temperature=0。全部数据来自本人实测,欢迎评论区交流踩坑经验。