GXF 报告 - 构建依赖分析,NvSci 裁剪,公网裸机构建与测试

GXF 4.1 全解析:依赖分析 · NvSci 裁剪 · 无内网裸机构建与测试

项目 :NVIDIA GXF(Graph eXecution Framework)4.1 / Isaac ROS 3.2.0

环境 :Ubuntu 22.04.5 LTS x86_64 · Tesla V100 32GB · 无 NVIDIA 内网权限

日期 :2026-08-28

工作目录tmp04_gxf/gxf/ 原始仓 · gxf_without_nvsci/ 改造仓 · local_deps/ 本机依赖包 · build.sh 一键脚本)


目录

  1. 分析篇
    • 1.1 GXF 是什么
    • 1.2 构建依赖全景(内网 vs 公网)
    • 1.3 NvSci 功能关联分析
  2. 裁剪篇
    • 2.1 裁剪策略
    • 2.2 完整改动清单
  3. 原理篇
    • 3.1 Bazel 外部依赖机制与惰性拉取
    • 3.2 公网替换的原理与 sha256 同源验证
    • 3.3 本机依赖注入:为什么必须 tar 重打包而非符号链接
    • 3.4 Bazel 补丁器严格性(boost.patch 重生)
    • 3.5 工具链环境变量注入原理
  4. 构建篇
    • 4.1 环境准备
    • 4.2 一键构建(build.sh
    • 4.3 手动构建命令
    • 4.4 构建产物
    • 4.5 排错记录(8 个问题)
  5. 测试篇
    • 5.1 构建验证
    • 5.2 运行时冒烟
    • 5.3 完整测试套件(891/892)
  6. 总结篇
    • 6.1 结论
    • 6.2 遗留事项与风险
    • 6.3 文件索引

一、分析篇

1.1 GXF 是什么

GXF 是 NVIDIA 的组件化计算图执行框架,是 Holoscan、DeepStream、Isaac ROS(NITROS) 等高性能 SDK 的运行时底座。本仓库(github.com/NVIDIA-ISAAC-ROS/gxf,公开,无子模块)包含 GXF 框架可构建源码与 Isaac ROS NITROS 的部署脚本。

构建系统 Bazel 6.0.0 (主,.bazelversion 锁定)+ CMake 3.24(辅)
源码主体 com_nvidia_gxf/gxf/ 框架、engine/ 工具链与脚本、registry/third_party/
Release 流程 build_install_gxf_release.shrelease/make_tarball.pybazel build ... --config={平台} → 收集 .so/头文件打 tar 包
平台配置 x86_64_cuda_12_2 / x86_64_cuda_12_6(默认)/ rhel9 / sbsa(hp21ea·ga) / jetpack60·61.bazelrc

1.2 构建依赖全景(内网 vs 公网)

核心结论:仓库公开 ≠ 可构建。 Bazel 构建所需的几乎全部外部依赖(约 50 个归档 + 2 个 Docker 基础镜像,全仓 94 处引用)指向 NVIDIA 内网 Artifactory urm.nvidia.com ,且 gxf/repo.bzlnv_gxf_http_archive() 只是原生 http_archive 的 license 包装,无公网回退逻辑

依赖四分法
类别 典型内容 对策方向
A. 闭源/内部重打包(不可公网下载) CUDA+cuDNN 重打包(7 平台)、NVCC 重打包、NvSci(来自 \\netapp39 内网服务器)、UCX 预编译包(8 变体)、定制 Python 运行时、aarch64 交叉编译器、Coverity(商业)、Graph Composer core 本机安装替代(C 类)或裁剪
B. 开源镜像(内网镜像了公网包) nlohmann-json、dlpack、gtest、breakpad、lss、magic_enum、fmt、spdlog、rmm、pybind11、cpprestsdk、boost、boringssl、grpc、protobuf、rules_go/docker/python/pkg/skylib/gazelle、compdb、rules_boost 换回公网源(D 类),注释中有 GitHub 出处
C. Docker 镜像 urm.nvidia.com/sw-gxf-docker/{gxf-build,gxf-l4t-build}(= nvcr.io/nvidian/* -image 容器目标需要,绕行
D. 已是公网 yaml-cpp、gflags(developer.nvidia.com)、apt/PyPI/CuPy wheel、nvcr.io/nvidia/cuda 基底 无需处理
其他内网点

install_dependencies.sh 下载 nvsci deb;.bazelrc 远程执行集群 grpc://10.178.201.18:32351(内网 IP);NGC_API_KEY(registry 扩展发布用,构建非必需)。

1.3 NvSci 功能关联分析

结论:NvSci 在全仓仅被 gxf/stream 一个扩展使用(libgxf_stream.so)。

使用点
复制代码
gxf/stream/
├── stream.cpp                 # 注册 StreamSyncId + StreamSync 两个组件
├── stream_nvsci.hpp           # Stream 纯抽象接口(不含任何 NvSci include)
├── stream_nvscisync.{cpp,hpp} # StreamSync:唯一的 NvSci 实现
└── tests/                     # StreamSync 单测 + CUDA dotproduct 测试扩展

Bazel 依赖仅 //third_party:nvscisync//third_party:nvscieventnvscibuf 本仓未使用,那是 Holoscan 生态的零拷贝 buffer 设施)。

StreamSync 组件职责

NvSciSync ↔ CUDA External Semaphore 互操作,实现 CUDA 流的跨 GPU/跨进程同步

接口 内部调用 功能
initialize() NvSciSyncModuleOpen 打开 NvSciSync 模块,校验 GPU ID
allocate_sync_object() cudaDeviceGetNvSciSyncAttributes + NvSciSyncAttrListReconcile 协商 signaler/waiter 属性并分配同步对象
signalSemaphore() / waitSemaphore() CUDA stream 对 NvSciSync fence 发信号/等待 跨流标记/等待任务完成
参数 signaler_gpu_id/waiter_gpu_id --- 多 GPU 场景

StreamSyncId 为图内消息结构,传递 StreamSync 组件 cid 让不同 codelet(甚至不同进程)共享同一同步关系。

内网成因与裁剪影响
  • x86 桌面版 NvSci 是 DRIVE/JetPack SDK Manager 内部产物(BuildBrain/L4T rel-36 构建),无公网发行渠道
  • Isaac ROS release 打包清单本就不含 libgxf_stream.so
  • 裁剪仅损失跨进程 CUDA 流硬件同步能力(Holoscan/DRIVE 场景),core/std/cuda/ucx/serialization/ipc/rmm 等全部不受影响。

二、裁剪篇

2.1 裁剪策略

并列副本 gxf_without_nvsci/ 上操作(原仓 gxf/ 保持不动),Bazel 与 CMake 两套构建系统同步清理;保留纯接口头 stream_nvsci.hpp(不含 NvSci 依赖,保持外部代码可编译),仅移除实现与依赖。

2.2 完整改动清单

共修改 19 个文件、删除 20 个文件:

类别 处理
Bazel 工作区 WORKSPACE / WORKSPACE.release:移除 nvsci_workspace() 加载与调用;third_party/BUILD:移除 nvsci_deps()
third_party 删除 nvsci.bzl、7 个 nvsci_*.BUILDcmake/nvsci.cmakecmake/modules/Findnvsci.cmake
gxf/stream 删除 stream_nvscisync.{cpp,hpp}stream.cpp 只注册 StreamSyncIdBUILD/BUILD.release/CMakeLists.txt 去除 nvsci 依赖与目标
gxf/stream/tests 删除 5 个测试源文件 + 2 个测试 yaml;gxe/BUILDgxe/tests/BUILDgxe/manifest.yamllibgxf_test_stream_sync_cuda.so 引用全部清除
CMake CMakeLists.txt(去 find_package(nvsci REQUIRED))、Superbuild.cmakeCMakePresets.json(去 nvsci_URL/HASH)、cmake/GXFConfig.cmake.inrelease/cmake/GXFConfig.cmake+GXFTargets.cmake(去 nvsci:: 链接与 test_stream_sync_cuda 导入目标)
脚本 engine/build/scripts/install_dependencies.shdocker/scripts/install_dependencies.sh:去除 nvsci deb 的 wget+dpkg 块;删除 nvsci_package_generation.sh
YAML/模板 build_gxf_release_content.yamlrelease/tarball_content.yaml:去 nvsci 条目;extension_repo/templates/WORKSPACE.tpl:去 nvsci_workspace()

验证 :全树无 nvsci_workspace / //third_party:nvsci* / find_package(nvsci) / nvsci:: / urm nvsci URL 残留(仅保留接口文件名 stream_nvsci.hpp 本身)。

三、原理篇

3.1 Bazel 外部依赖机制与惰性拉取

GXF 用 WORKSPACE 声明全部外部仓库(nv_gxf_http_archivehttp_archive)。Bazel 惰性拉取 :只有构建图实际到达的仓库才会被下载。因此 --config=x86_64_cuda_12_6 构建时:

  • ✅ 必须处理:x86_64 平台的 CUDA/NVCC/UCX/Python + 全部开源库(构建图内);
  • ✅ 天然规避:aarch64/rhel9/sbsa/jetpack 变体、docker 镜像、graph_core、其余平台工具链------无需任何处理

这把"约 50 个内网依赖"的实际工作量压缩到 ~30 个。

3.2 公网替换的原理与 sha256 同源验证

方法:下载 22 个公网归档逐一计算 sha256,与原 pin 值比对。结果:

  • 14 个与原值完全一致 (dlpack、gtest、grpc、protobuf、rmm、spdlog、cpprestsdk、rules_go、rules_docker、rules_python、bazel-skylib、bazel-gazelle、boringssl、compdb、rules_pkg)------证明 NVIDIA 直接从公网归档、未重打包,直接换 URL 即可
  • 6 个重算 (nlohmann-json、breakpad、pybind11、magic_enum、fmt、rules_boost------NVIDIA 重新打包过,需同步更新 strip_prefix/type);
  • 2 个特殊lss(googlesource +archive 动态生成,校验和不稳定 → 去除 pin,HTTPS 保证完整性);boost 1.80.0(URL 藏在 boost.patch 补丁里,见 3.4)。

3.3 本机依赖注入:为什么必须 tar 重打包而非符号链接

第一直觉方案 (失败):new_local_repository 指向符号链接森林(cp -rs 把本机 CUDA/UCX/Python 链进影子目录)。遭遇 Bazel 已知缺陷链:

  1. CUDA 安装内部含指回 targets/ 的符号链接(/usr/local/cuda/include → targets/x86_64-linux/include),头文件真实路径绕出声明输入;
  2. Bazel include 扫描器 (C++ 编译输入发现阶段,与沙箱无关,--spawn_strategy=standalone 也躲不掉)按真实路径校验声明输入 → 报 undeclared inclusion(s)
  3. local repo 的 glob 结果会被 Skyframe 缓存 ,换森林后不会自动重算(bazel sync 亦无效,需删 external/<repo> + marker)。

最终方案(与 NVIDIA 内部机制同构) :把本机安装抽取为 tar 包,用 http_archive + file:// 引入------真实解压文件、零符号链接,include 扫描器路径比对天然一致。

Bazel 仓库 来源 布局 大小
@cuda_x86_64_12060 /usr/local/cuda-12.8(12.8+cuDNN 9.10) usr/local/cuda-12.6/{include,lib64,targets}(仅取被引用 .so) 6.8 GB
@nvcc_12_06 /usr/local/cuda-12.8new_local_repository,仅作编译工具、不经 include 扫描,可直接用) bin/+nvvm/ ---
@ucx_x86_64_cuda_12_6 /opt/ucx-1.20.0 ucx-install-with-cuda/usr/{lib,include} 43 MB
@python_x86_64_3_10 系统 python3.10 头文件 + libpython3.10.so include/python3.10 + lib/... 2.5 MB
@coverity 空壳 stub(_coverity_* 目标默认 run_coverity=False,仅分析期需要仓库存在) 空 filegroup 8 KB

3.4 Bazel 补丁器严格性(boost.patch 重生)

boost.patch 把 rules_boost 的 boost 1.80.0 下载地址改到了内网。重写时踩坑:GNU patch 容忍模糊块与错误 @@ 行号(NVIDIA 原补丁的行号本就是错的),而 Bazel 内置 Java 补丁器严格匹配 ,且要求每个文件段前有 diff --git 分隔行。解法:对原始归档重新 diff -u 生成补丁、剥去 ---/+++ 行时间戳、补 diff --git 行。补丁同时恢复 boost 公网地址(archives.boost.io + sourceforge)与上游 sha256 4b2136f9...,并保留 @openssl@boringssl 重命名(复用 ipc.bzl 已定义的 boringssl)。

3.5 工具链环境变量注入原理

engine/build/toolchain/toolchain.bzltoolchain_configure 仓库规则通过 repository_ctx.os.environ 读取 target_platform/cpu/compiler.bazelrc 里的 action_env 不影响仓库规则求值,因此构建时必须:

bash 复制代码
target_platform=x86_64_cuda_12_6 cpu=k8 compiler=gcc-11 \
  bazel build ... --repo_env=target_platform=x86_64_cuda_12_6 \
                --repo_env=cpu=k8 --repo_env=compiler=gcc-11

(已内嵌进 build.sh,用户无感。)

四、构建篇

4.1 环境准备

本机情况 备注
Ubuntu 22.04 / gcc-11 / Python 3.10 toolchain 直接用 /usr/bin/gcc-11
CUDA + cuDNN ✅ 12.8 + 9.10(期望 12.6+9.3,验证兼容) 重打包注入
UCX ✅ 1.20 @ /opt/ucx-1.20.0(期望 1.17,API 兼容) 重打包注入
TensorRT 10.9 ✅ 但 GXF 不使用,无需处理 ---
Bazel 6.0.0 已备于 tmp04_gxf/tools/bazel(公网 GitHub 二进制) 脚本自动补齐
Python 测试依赖 pip install --user numpy==1.23.5 cupy_cuda12x-13.3.0 官方 docker 同款组合;装入 ~/.localpip uninstall numpy 可恢复系统 numpy 2.2.6)

4.2 一键构建(推荐)

tmp04_gxf/build.sh(幂等;自动检查/生成 Bazel 与本机依赖 tar 包):

bash 复制代码
cd /home/ruler/ex_gxf/tmp04_gxf
./build.sh        # 一键构建 35 个 release 目标
./build.sh test   # 构建 + gxe 运行时冒烟测试
./build.sh package           # 打包 release tarball(复用仓内 make_tarball.py)
./build.sh install <prefix>  # 安装头文件+预编译库到指定前缀
./build.sh deps   # 重新生成本机依赖 tar 包(CUDA/UCX/Python)
./build.sh clean  # bazel clean

首编约 1~2 小时(含公网下载与 grpc 编译),之后增量构建秒~分钟级。

4.2.1 打包与安装(2026-08-28 补充)

打包./build.sh packagedist/gxf_isaac_release.tar.gz(~46 MB)。复用仓内 release/make_tarball.py,为适配无内网环境做了两处小改:

  1. bazel build ...(全目标,会拉内网容器镜像/coverity)→ 改为按 yaml 目标清单显式构建 35 个目标
  2. 多平台时的 bazel clean--single_platform x86_cuda_12_6 时跳过(保住增量缓存,且规避需要内网交叉编译器的 jetpack61)。

tarball 内容 = 公开头文件树 + 平台预编译库(tmp/gxf-release/gxf_x86_64_cuda_12_6/**.so + BUILD.release 文件)+ gxe + manifest + coverity 空实现,与上游 Isaac ROS release 结构一致。

安装./build.sh install <prefix>,布局与上游 isaac_ros_gxf/gxf/core 相同:

复制代码
<prefix>/
├── include/{common,gxf,third_party}/...   # 148 个头文件(上游同款 rsync 过滤规则)
└── lib/gxf_x86_64_cuda_12_6/
    ├── {core,std,cuda,ipc,...}/libgxf_*.so  # 15 个扩展(SONAME 已用 patchelf 写入)
    └── gxe/{gxe,manifest.yaml}              # manifest 已按实际存在的扩展裁剪

已实测:install/lib 下的 gxe + manifest + .so 完整执行 test_ping.yaml(100 entities 收发,正常退出)。装到系统位置示例:./build.sh install /opt/gxf(需写权限);装给 Isaac ROS:./build.sh install .../isaac_ros_nitros/isaac_ros_gxf/gxf/core。patchelf 经 pip install --user patchelf 提供,无需 sudo。

4.3 手动构建命令

bash 复制代码
cd gxf_without_nvsci/com_nvidia_gxf
target_platform=x86_64_cuda_12_6 cpu=k8 compiler=gcc-11 \
  bazel build //gxf/... --config=x86_64_cuda_12_6 \
  --repo_env=target_platform=x86_64_cuda_12_6 \
  --repo_env=cpu=k8 --repo_env=compiler=gcc-11

(注意://gxf/... 通配会带上 -image 容器目标------内网镜像 + 上游 puller 已失效,build.sh 改用精确的 35 目标清单规避。)

4.4 构建产物

位置 说明
gxf_without_nvsci/com_nvidia_gxf/bazel-bin/gxf/ 便捷符号链接(推荐入口)
~/.cache/bazel/_bazel_ruler/<hash>/execroot/com_nvidia_gxf/bazel-out/k8-opt/bin/ 真实输出根(约 3.5 GB)

25 个 .so + gxe 可执行文件,覆盖 core/std/cuda/ucx/ipc(grpc·http)/npp/multimedia/network/serialization/python_codelet/rmm/sample/logger/behavior_tree/stream(裁剪版) 全模块。

4.5 排错记录(8 个问题)

# 现象 根因 解法
1 toolchain 读不到平台变量 仓库规则读 os.environaction_env 无效 --repo_env + 前置环境变量(见 3.5)
2 //gxf/... 强拉 @coverity 宏自动生成 _coverity_* 目标 空壳 stub(见 5.3 表)
3 -image 容器目标失败 内网镜像;rules_docker 0.22 的 puller 公网 URL 已 403(上游自身坏) 容器目标移出构建范围
4 @boost 拉内网 URL 被 boost.patch 改写 重生补丁(见 3.4)
5 新补丁被拒 Incorrect Chunk Bazel Java 补丁器严格匹配 + 需 diff --git 对原归档重打 diff(见 3.4)
6 lss 校验和报错 googlesource +archive 动态生成 去除 sha256 pin
7 rmm 等 undeclared inclusion(s) local_repository 符号链接 vs include 扫描器;glob 缓存 tar 重打包根治(见 3.3)
8 standalone 策略无法绕过 #7 该校验发生在输入发现阶段,与沙箱无关 同 #7

五、测试篇

5.1 构建验证

bazel build <35 个 release 目标>35/35 全部产出(含 nvcc 12.8 编译的 CUDA 扩展、本机 UCX 1.20 的 ucx 扩展、系统 Python 的 python_codelet、裁剪版 libgxf_stream.so)。

5.2 运行时冒烟

bash 复制代码
bazel run //gxf/gxe:gxe ... -- --app=gxf/test/apps/test_ping.yaml --manifest=gxf/gxe/manifest.yaml

✅ gxe 加载扩展清单,tx/rx 各 100 entities 收发正常,Job Statistics 输出完整,上下文正常销毁。

5.3 完整测试套件(Tesla V100 环境)

bazel test //gxf/... --build_tests_only --keep_going(892 个测试:263 cpplint、238 coverity 空转、31 CUDA GPU 测试、26 UCX 测试等)。

轮次 结果
首轮(并行) 882/892 通过,10 失败
修复 + 串行复跑 891/892 通过(99.9%)

10 个失败根因(均与 NvSci 裁剪、依赖替换无关):

失败 根因 处置
8 个 //gxf/python/tests/*(test_core、tensor 系列、cpu_fft、dlpack×2) cupy(官方 docker 预装 cupy_cuda12x 13.3.0 + numpy 1.23.5) pip 补齐后全过
test_ping_faster_producer_yaml 时序敏感:并行负载下窗口内 1684 < 期望 ≥1800 条 串行复跑通过(负载抖动)
crash_backtrace_print 上游快照自带过时测试 :期望 "==== backtrace (tid:",该字符串在原始仓库全部源码中无任何产生处;当前崩溃行为为写 breakpad minidump(实测正常) 判定 upstream known-broken,保留

六、总结篇

6.1 结论

  1. GXF 4.1 可在无 NVIDIA 内网环境完成源码构建与全量测试:~50 个内网依赖中 x86_64 实际所需 ~30 个全部可由"公网源(14 个 sha256 同源验证 + 6 个重算)+ 本机 tar 重打包"替代,其余被 Bazel 惰性拉取天然规避。
  2. NvSci 裁剪安全收敛 :仅影响 gxf/stream 的 StreamSync 组件(跨进程 CUDA 同步),Isaac ROS release 本就不含它;裁剪版 libgxf_stream.so 正常构建。
  3. 本机较新版本依赖可行:CUDA 12.8(期望 12.6)、UCX 1.20(期望 1.17)、cuDNN 9.10(期望 9.3)构建与 891 项测试全部验证通过。
  4. 本机依赖注入 Bazel 的正确姿势是 tar 重打包 + http_archive ,切勿 local_repository + 符号链接(include 扫描器真实路径校验缺陷)。
  5. 一键化已落地build.sh 幂等封装全部环境准备与构建参数。

6.2 遗留事项与风险

说明
版本差异 CUDA 12.8/UCX 1.20 当前全部验证可用;若后续 nvcc 12.8 编译旧代码出错,退路为公网 CUDA 12.6 runfile 独立前缀
nvgraph CUDA 12.8 已移除,glob 为空;当前无 target 实际链接它
lss 无校验和 以 HTTPS 传输完整性替代;如需固定可自建镜像
未覆盖项 -image 容器目标(内网镜像 + 上游 puller 失效)、@graph_core(Composer UI)------均不影响 release 构建
crash_backtrace_print 上游自带坏测试,保持失败(1/892)
jetpack61 本次仅验证 x86_64;aarch64 需另换交叉编译器与 arm64 各包
系统 numpy ~/.local 的 1.23.5 遮蔽(cupy 需要),pip uninstall numpy 可恢复

6.3 文件索引

复制代码
tmp04_gxf/
├── gxf/                       # 原始仓库(未改动)
├── gxf_without_nvsci/         # NvSci 裁剪 + 公网化改造(构建/测试全部通过)
│   └── com_nvidia_gxf/
│       ├── WORKSPACE*         #   去 nvsci;compdb 公网化
│       ├── third_party/       #   gxf.bzl(18 处)/cuda.bzl/ucx.bzl/ipc.bzl 公网化;boost.patch 重生
│       ├── gxf/stream/        #   裁剪核心现场
│       └── ...
├── local_deps/dist/           # 本机依赖 tar 包(cuda 6.8GB / ucx 43MB / python 2.5MB)
├── tools/bazel                # Bazel 6.0.0
├── build.sh                   # 一键构建脚本
├── gxf_Analysis.md            # 第一版报告
└── gxf_Analysis02.md          # 本文档

全文完

相关推荐
众人皆醒我独醉2 天前
InferenceGraph:把多个推理服务编排成 DAG
面试·kubernetes·gpu
众人皆醒我独醉2 天前
LLMService:KServe 面向 LLM 的下一步
面试·kubernetes·gpu
众人皆醒我独醉4 天前
模型加载:storage-initializer 与节点级缓存
面试·kubernetes·gpu
进哥AI研习社4 天前
WebGL 与视频背景——首屏视觉层的性能博弈
性能优化·webgl·gpu·canvas·shader·帧率·视频背景
众人皆醒我独醉5 天前
调和循环:kubectl apply 之后发生了什么
面试·llm·gpu
众人皆醒我独醉5 天前
从 Spec 到资源:Predictor/Transformer/Explainer 如何变成 Deployment
面试·llm·gpu
众人皆醒我独醉9 天前
源码导读:一张地图看懂 KServe 仓库
面试·云计算·gpu
众人皆醒我独醉10 天前
MLOps 流水线:从训练到推理的 CI/CD
面试·llm·gpu
Flynt11 天前
本地跑大模型到底选哪个?我用llmfit把32000星的项目实测了一遍
ai编程·gpu·ollama