3. 环境搭建:交叉编译工具链、嵌入式Linux系统配置、TensorRT-LLM依赖库安装

3.1 核心原理:为什么不能直接在目标板上编译?

嵌入式设备(如 Jetson Orin、Xavier NX 或自定义 ARM 板卡)的算力、内存和存储资源通常远小于 x86 开发工作站。若直接在目标板上执行 TensorRT-LLM 的完整编译流程,会遇到三个致命瓶颈:CPU 核数少导致编译耗时数小时、内存不足导致 OOM 崩溃、文件系统空间不足以容纳中间产物。因此,业界标准做法是采用交叉编译:在 x86_64 主机上使用目标架构的交叉工具链,生成 ARM64(aarch64)架构的可执行文件与共享库,再通过 SSH/ADB 或 U 盘部署到嵌入式 Linux 系统上运行。

交叉编译的核心在于三套分离的环境:构建环境(x86_64 主机)、目标环境(aarch64 嵌入式 Linux)、sysroot(目标板的根文件系统镜像,包含头文件与预编译库)。TensorRT-LLM 依赖 CUDA、cuDNN、TensorRT 等 NVIDIA 闭源库,这些库必须从 NVIDIA 官网下载对应 aarch64 版本并放入 sysroot,而不能直接使用主机上的 x86_64 版本。

关键认知:交叉编译不是“在主机上编译出 ARM 文件”这么简单,而是要让编译器、链接器、依赖库、头文件、运行时路径全部指向目标架构。任何一环错位(例如误用了 x86 的 libcuda.so),都会导致链接失败或运行时段错误。
构建环境 (x86_64 主机) 交叉工具链 aarch64-linux-gnu-g++ 12.3 TensorRT-LLM 源码 git clone --branch v0.10.0 sysroot (aarch64) CUDA 12.2 / cuDNN 8.9 TensorRT 8.6 / OpenMP CMake 交叉配置 toolchain.cmake 编译产物: .so / 可执行文件 SCP/ADB 目标环境 (aarch64 嵌入式) 嵌入式 Linux 系统 Ubuntu 22.04 (JetPack 5.1) 运行时依赖 libcuda.so / libnvinfer.so 部署目录 /opt/tensorrt-llm/lib /opt/tensorrt-llm/bin 环境变量 LD_LIBRARY_PATH=/opt/tensorrt-llm/lib 运行验证: trtllm-build 验证与排障 ldd 检查 确认无 "not found" 架构检查 file libtensorrt_llm.so 小模型冒烟 GPT-2 单层推理 性能基线 tokens/sec 记录 迭代优化

3.2 方法流程:交叉编译工具链与 sysroot 准备

首先确认目标板架构与系统版本。以 Jetson Orin NX 16GB 为例,其 CPU 为 Arm Cortex-A78AE(aarch64),推荐使用 JetPack 5.1.2(对应 Ubuntu 22.04)。主机侧安装交叉工具链:

# 主机 (x86_64 Ubuntu 22.04)
sudo apt update
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# 验证版本
aarch64-linux-gnu-g++ --version   # 应显示 11.4 或更高

# 创建 sysroot 目录结构
mkdir -p ~/tllm_sysroot/{usr/include,usr/lib/aarch64-linux-gnu,lib}
cd ~/tllm_sysroot

# 从目标板同步系统头文件与基础库(需在目标板上执行 rsync)
# 目标板执行:
# rsync -avz /usr/include/ user@host:~/tllm_sysroot/usr/include/
# rsync -avz /usr/lib/aarch64-linux-gnu/ user@host:~/tllm_sysroot/usr/lib/aarch64-linux-gnu/
# rsync -avz /lib/aarch64-linux-gnu/ user@host:~/tllm_sysroot/lib/

接下来下载 NVIDIA 官方 aarch64 依赖库。注意:不能从 pip 或 apt 直接安装 x86 版本,必须从 NVIDIA 官网 选择 Linux aarch64 的 CUDA Toolkit、cuDNN 和 TensorRT 的 .deb 或 .tar.gz 包。解压后,将 lib 目录下的 .so 文件复制到 sysroot 的 usr/lib/aarch64-linux-gnu/ 下,头文件复制到 usr/include/ 对应位置。

依赖库 版本要求 关键文件 放置位置 (sysroot)
CUDA Toolkit ≥ 12.2 libcudart.so, libcuda.so usr/lib/aarch64-linux-gnu/
cuDNN ≥ 8.9 libcudnn.so.8 usr/lib/aarch64-linux-gnu/
TensorRT ≥ 8.6 libnvinfer.so, libnvinfer_plugin.so usr/lib/aarch64-linux-gnu/
OpenMP 随 GCC 提供 libgomp.so.1 usr/lib/aarch64-linux-gnu/

3.3 案例:TensorRT-LLM 的 CMake 交叉编译配置

TensorRT-LLM 官方支持交叉编译,但需要显式指定工具链文件。以下是一个经过验证的 toolchain.cmake 示例:

# toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

set(CMAKE_SYSROOT $ENV{HOME}/tllm_sysroot)
set(CMAKE_FIND_ROOT_PATH $ENV{HOME}/tllm_sysroot)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# 强制使用静态链接 libstdc++ 避免目标板缺库
set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++ -static-libgcc")

然后执行编译(以 TensorRT-LLM v0.10.0 为例):

cd ~/TensorRT-LLM
mkdir build && cd build

cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake \
  -DCMAKE_BUILD_TYPE=Release \
  -DTRT_LIB_DIR=$HOME/tllm_sysroot/usr/lib/aarch64-linux-gnu \
  -DTRT_INCLUDE_DIR=$HOME/tllm_sysroot/usr/include \
  -DCUDNN_LIB_DIR=$HOME/tllm_sysroot/usr/lib/aarch64-linux-gnu \
  -DCUDNN_INCLUDE_DIR=$HOME/tllm_sysroot/usr/include \
  -DCUDA_TOOLKIT_ROOT_DIR=$HOME/tllm_sysroot/usr/local/cuda

make -j$(nproc) trtllm-build
常见错误:若出现 cannot find -lcuda,说明 CUDA 的 .so 文件未正确放入 sysroot。请检查 libcuda.so 是否为符号链接且指向真实存在的 libcuda.so.1。另外,TensorRT-LLM 部分组件(如 pybind11)需要 Python 头文件,若不需要 Python 绑定,务必添加 -DBUILD_PYTHON_BINDINGS=OFF 以缩短编译时间。

3.4 嵌入式 Linux 系统配置与依赖安装

编译完成后,将产物部署到目标板。目标板需要预先安装以下运行时库(注意是 aarch64 版本):

# 在目标板上执行
sudo apt update
sudo apt install -y libgomp1 libpython3.10 libssl3 libcurl4

# 创建部署目录并拷贝文件(从主机执行)
scp -r ~/TensorRT-LLM/build/lib/*.so user@target:/opt/tensorrt-llm/lib/
scp ~/TensorRT-LLM/build/trtllm-build user@target:/opt/tensorrt-llm/bin/

# 在目标板上设置环境变量
echo 'export LD_LIBRARY_PATH=/opt/tensorrt-llm/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
echo 'export PATH=/opt/tensorrt-llm/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

验证部署是否成功:

# 目标板上执行
ldd /opt/tensorrt-llm/bin/trtllm-build | grep "not found"
# 若无输出,说明所有依赖已满足

file /opt/tensorrt-llm/lib/libtensorrt_llm.so
# 应输出: ELF 64-bit LSB shared object, ARM aarch64

# 运行冒烟测试
trtllm-build --help | head -5

3.5 误区与复盘:交叉编译中的典型陷阱

  • 误区一:使用 pip 安装 TensorRT-LLM 的 x86 版本后直接拷贝 .so 到目标板。 这会导致 Exec format error,因为 x86 与 ARM 的 ELF 格式不兼容。复盘:必须从源码交叉编译,或使用 NVIDIA 官方提供的 aarch64 wheel 包(若有)。
  • 误区二:忘记同步 sysroot 中的 libstdc++。 主机 GCC 12 的 libstdc++ 版本高于目标板 GCC 11,导致 GLIBCXX_3.4.30 not found。复盘:在 toolchain.cmake 中增加 -static-libstdc++,或同步目标板的 libstdc++.so.6 到 sysroot。
  • 误区三:CUDA 库版本不匹配。 TensorRT-LLM 编译时链接 CUDA 12.2,但目标板 JetPack 自带 CUDA 11.4,导致运行时 libcudart.so.12 not found。复盘:务必确认目标板的 CUDA 版本与编译时一致,必要时在目标板安装 CUDA 12.2 的 runfile(仅安装 toolkit,不覆盖驱动)。
  • 误区四:忽略 OpenMP 线程数设置。 嵌入式 CPU 核数少,默认线程数可能过高导致上下文切换开销。复盘:在推理脚本中设置 OMP_NUM_THREADS=4(根据实际核数调整),并观察 nvidia-smi 的 GPU 利用率与 CPU 负载平衡。
复盘建议:在正式移植前,先编译一个最小化的 C++ 程序(仅调用 TensorRT API 创建 engine),部署到目标板验证运行环境。这能将“环境问题”与“业务代码问题”隔离,大幅缩短排障时间。记录每次编译的 CMakeCache.txt 中的关键路径变量,便于回溯。