Tensor Runtime 生产指南¶
本页是 scoped 单 GPU G1 Motion Tracking / FlashSAC / MJWarp tensor runtime 的复现与运维入口,覆盖 canonical owner-routed 长 soak、runtime artifact 与 故障排查。设置契约见 Tensor runtime;所有 backend、task、入口与 平台支持声明的唯一权威是生成的 后端支持矩阵。
生产路径刻意保持很窄:
Linux 与一块 NVIDIA CUDA GPU;
FlashSAC;
g1_motion_tracking;通过统一 task-owner 路由选择的
mjwarpowner;Manager runtime 默认 tensor-native。
它不是多 GPU 指南,不发布软件包,也不能推广为所有 MJWarp task 的通用 声明。macOS 与 ROCm/HIP 没有该 device-resident lifecycle 的 fallback; 非法请求会在 backend 构造前失败。
Owner 选择与有效组合¶
--algo、--task 与 --sim 必须一起使用。它们组合
src/unilab/conf/<algo>/task/<task>/<sim>.yaml 下的 owner YAML。真正声明
training.sim_backend 的是该 owner,而不是 CLI 拼写本身;tensor 执行是 Manager 不变量。
canonical 组合使用:
uv run train --algo flashsac --task g1_motion_tracking --sim mjwarp
MJWarp owner 继承 FlashSAC MuJoCo owner,并解析为:
有效设置 |
值 |
|---|---|
Task 语义名 |
|
Backend 身份 |
|
Tensor runtime |
Manager 生命周期不变量 |
Inference slot |
|
Replay-ingress depth |
|
Replay-ingress slot rows |
|
Collector metric interval |
|
CUDA 进程共享 |
|
Scene |
|
Motion |
|
MuJoCo owner 使用同样的 2048 环境训练预算,但每步发布 collector metric。 MJWarp task 也是 DR-free lifecycle baseline:其 selected-row tensor reset 不协商 reset randomization 或 interval wrench。
在当前生成证据等级中,FlashSAC / g1_motion_tracking / MJWarp 是
Configured。本地 soak 成功不会自动把它提升为 Benchmarked 或
Recommended;支持等级提升必须通过 support-matrix generator 及其
validation inventory 单独决策。
可复现 Runtime¶
gated profile 通过已提交的 registry lock 文件解析 UniSim 与
MJBatch(RL 栈已内置于 unilab.rl):
UV_FROZEN=1 uv sync --extra mujoco --extra mjwarp
make setup 适合日常仓库开发,但不安装 mjwarp extra。上面的命令与 gated
CUDA CI profile 一致。虽然包支持更宽的 Python 范围,CI 以 Python 3.11 作为
参考版本。
只可见一块 GPU 的主机无需设置 CUDA_VISIBLE_DEVICES。自动单设备拓扑会把
learner、collector、MJWarp 物理后端、inference ring 与 replay ingress 绑定到
同一个当前 CUDA 设备。只有在多 GPU 主机上选择一块物理 GPU,或需要保留
launcher/debug/MPS 掩码时才设置该变量:
export CUDA_VISIBLE_DEVICES=<single-host-cuda-ordinal>
trainer 进程内所有 CUDA ordinal 都相对于该 mask。不要添加更多设备:M11 仅 覆盖单 GPU。
CUDA MPS 执行共享¶
training.cuda_process_sharing 是针对既有 rank-local 拓扑的显式
execution-sharing 模式。它不是 backend 开关,也不替代 --sim mjwarp 或 owner
YAML 选择。
默认 null 表示 learner 与 collector 保持独立 CUDA context。初始支持拓扑为
Linux、NVIDIA CUDA、单物理 GPU、MJWarp、SAC/FlashSAC 且 world_size=1。使用以下
设置请求共享 GPU 执行:
training.cuda_process_sharing=mps
受管理的 daemon 生命周期¶
UniLab 提供显式的用户自有生命周期命令,但训练路径仍不会隐式启动或停止 host 服务:
单 GPU 主机无需传任何参数,默认值已经完整:
uv run uni-cumps start
uv run uni-cumps doctor
uv run uni-cumps stop
start 后,设置 training.cuda_process_sharing=mps 的 trainer 会自动选择
覆盖该 GPU 的唯一 live UniLab-recorded daemon。Shell 集成变为可选:
uv run uni-cumps start
uv run --extra mjwarp train --algo flashsac \
--task g1_motion_tracking --sim mjwarp \
training.cuda_process_sharing=mps
调用方显式提供的 CUDA_MPS_PIPE_DIRECTORY 仍然优先。若没有匹配的 live
daemon,训练会在 environment/learner/collector 构造前 fail closed,并指向
uni-cumps start。
默认参数:
设置 |
默认值 |
|---|---|
GPU |
唯一可见的物理 GPU,并解析为 canonical UUID |
daemon 名称 |
|
pipe 目录 |
|
log 目录 |
|
|
唯一 live 的 UniLab-recorded daemon |
如果可见 GPU 多于一张,start 与 doctor 必须显式传
--gpus <index-or-uuid>。需要稳定 label 或服务自有路径的部署仍可显式覆盖:
uv run uni-cumps start \
--gpus <gpu-index-or-uuid> \
--name <daemon-name> \
--pipe-dir /absolute/path/mps/pipe \
--log-dir /absolute/path/mps/log
eval "$(uv run uni-cumps env --name <daemon-name>)"
uv run uni-cumps stop --name <daemon-name>
只读 status 无参数即可使用:
uv run uni-cumps status
status 与 doctor 是只读诊断。start 创建 user-owned daemon,并在当前用户的
UniLab cache 中写入 record。stop 只接受 record 中的 ownership evidence:UID、
host、control PID 与 Linux process start-time;它绝不会向 unmanaged pipe 发送
quit。env 输出给 launcher 或显式 shell 集成使用的环境变量,不修改 UniLab
父进程环境。
daemon 不是某个训练 run 的独占资源,可以服务多个 client。stale record 会在复用前 被 quarantine;stop 后保留日志。
拓扑模式与当前限制¶
命令会报告三个 topology-mode 名称,使未来 launch contract 保持加法扩展:
模式 |
含义 |
初始状态 |
|---|---|---|
|
一个任务使用一张物理 GPU |
已实现 |
|
一个任务每张 GPU 一个 rank |
可解析,fail-closed |
|
多个独立任务按 GPU 打包 |
可解析,fail-closed |
selector 会解析成 canonical 物理 GPU UUID。MIG UUID 显式拒绝。逗号分隔的多 GPU
请求会 fail closed,并指向 multi-GPU gate,而不是静默选择 DP 或 task packing。
all 可用于只读诊断,但当前版本不能用它启动 daemon。
训练仍在 environment probe、learner construction 与 collector spawn 之前验证; 没有静默 multi-context fallback。错误会指出第一个未满足条件,并指向 CLI 生命周期 命令。
run_config.json 记录配置值。有效 run 的 run_summary.json 内嵌
runtime_manifest.cuda_process_sharing,包含 configured/effective 模式、learner
与 collector device、物理 UUID 证据、control pipe、server PID 与 validation 状态。
未配置(null)的 run 在 CUDA rank 上仍会记录环境级 host 状态——
CUDA_MPS_PIPE_DIRECTORY 原始值、生效的 pipe 目录与 control pipe 类型、应答的
server PID,以及 best-effort 的 learner/collector GPU UUID——当环境级 daemon 在
生效 control pipe 上应答时 ambient_server=true,使透明走 MPS 的 run 可以从自身
manifest 自证。ambient 观测永不 fail closed;无法解析的事实记录为 null。
该 section 是 runtime-manifest v1 的 producer diagnostic,不是稳定标量契约。
MPS 只改变 GPU execution sharing,不改变 env_steps_per_sync、
learner/collector placement 或训练语义。多 GPU DP 在独立的 gate 与 daemon 拓扑
决策完成前保持不支持。CUDA MPS 仍依赖 host 与部署方式:在共享容器、多用户主机或
受限 runner 中 control 可能不可用,显式请求会 fail closed 而不是静默降级。
无法使用 UniLab CLI 的受限部署仍可以用 nvidia-cuda-mps-control 手动运行既有
daemon;训练验证不关心该 daemon 由哪个显式部署工具启动。
检查实际执行 benchmark 的 runtime:
uv run --no-sync python -c "import torch; print(torch.__version__, torch.version.cuda, torch.version.hip, torch.cuda.is_available(), torch.cuda.device_count(), torch.cuda.current_device())"
torch.version.hip 必须为 None,CUDA 必须可用,且可见设备数必须为 1。
后端 CUDA Graph 诊断¶
训练 artifact 会记录物理 backend 的 tensor runtime 诊断。
TorchEnv.get_backend_runtime_diagnostics()(src/unilab/base/torch_env.py)
是采集边界:一个 public cold-path 方法,把 backend 的
SimBackend.get_tensor_runtime_diagnostics()(unisim 公开契约)序列化为
纯 JSON。off-policy collector(unilab.rl.runtime.offpolicy)在启动时调用它,并把 payload 发布到
runtime_manifest.backend_diagnostics;PPO 没有 runtime manifest,因此
PPO 训练脚本(src/unilab/scripts/train_rsl_rl.py)把同样的 payload 作为
顶层 backend_diagnostics 字段写入 run_summary.json。
payload 形状:
"backend_diagnostics": {
"available": true,
"backend_type": "mjwarp",
"diagnostics": {
"cuda_graph": {"requested": true, "enabled": false, "disable_reason": "..."},
"selected_reset_sensor_refresh": {"requested": true, "enabled": true, "disable_reason": null},
"device_reset_randomization": {"requested": true, "enabled": true, "disable_reason": null}
}
}
requested:该优化已被请求(MJWarp 总是请求 CUDA graph)。enabled:该优化实际生效(例如 fixed-address CUDA graph replay)。disable_reason:backend 自有的 fallback 原因;enabled为 false 时 必为非空字符串,enabled为 true 时必为null。
当 env 或 backend 不提供诊断时,payload 是显式的 unavailable 标记,绝不
是裸 null:
"backend_diagnostics": {"available": false, "backend_type": "...", "unavailable_reason": "..."}
与 runtime_manifest.cuda_process_sharing 一样,该 section 是
runtime-manifest v1 的 producer diagnostic,不是稳定标量契约;runtime
manifest schema(unilab.rl.logging.runtime_manifest_schema)
在该 section 存在时会 fail-closed 校验。这些诊断
仅用于 observability:不改变 CUDA graph 启用策略,也不是验收 gate。它们是
collector/env 启动时采集的 cold-path 快照,描述初始化状态,不描述 per-step
行为。
现象 |
首要处理 |
|---|---|
某项优化 |
读取 |
|
查看 |
已知限制:unisim 尚未暴露 CUDA graph 版本或 eager-fallback 事件计数。 serializer 会自动透传未来的 dataclass 字段,因此该 section 会随 unisim 的 扩展而增长。
资产¶
G1 mesh 与纹理来自 asset hub。离线 benchmark 前先预取:
uv run --no-sync unilab-pull-assets --robot g1
motion clip 同样不在 Git 中。它会在首次使用时惰性下载,并必须存在于:
src/unilab/assets/motions/g1/dance1_subject2_part.npz
完全离线运行前,先物化所需 motion 文件,再设置 HF_HUB_OFFLINE=1。首次
物化完成前不要开启 offline 模式。
Canonical 长 Soak¶
长 soak 是 canonical production benchmark。它启动真实 owner-routed FlashSAC trainer,并监控 schema、进度、CUDA 状态、进程、file descriptor、 shared memory、inference flight、replay ingress 与 shutdown。
为 run 目录和 JSON artifact 选择全新的绝对路径。不要复用脏 run 目录: monitor 使用该目录中的文件变化作为 liveness 信号。
uv run --no-sync scripts/benchmark/torch_env/g1_flashsac_soak.py \
--num-envs 1024 \
--iterations 65000 \
--min-duration-seconds 1800 \
--sample-interval-seconds 5 \
--startup-timeout-seconds 900 \
--stale-progress-seconds 300 \
--post-shutdown-grace-seconds 10 \
--extra-override algo.save_interval=10000 \
--log-dir /absolute/path/to/g1-flashsac-mjwarp-run \
--output /absolute/path/to/g1-flashsac-mjwarp-soak.json
wrapper 拥有并拒绝覆盖 algo、task、training.sim_backend、play 模式、
log 目录、环境数与迭代数。它为 flashsac / g1_motion_tracking /
mjwarp 构造底层 owner 路由;用户不应把
training.sim_backend=mjwarp 当作独立开关传入。
M11 验收预算刻意不同于 owner 生产默认值:
预算 |
环境数 |
迭代数 |
用途 |
|---|---|---|---|
MJWarp owner 默认 |
2048 |
25000 |
Owner 训练预算 |
M11 长 soak |
1024 |
65000 |
lifecycle、资源、replay 与 shutdown 验收 |
wrapper 还设置 training.no_play=true,因此通过的 soak 不会生成 playback
视频或 playback 驱动的 ONNX。
必需 Artifact¶
run 目录必须包含:
run_config.json;run_summary.json;TensorBoard event 文件;
按所选
algo.save_interval生成的 checkpoint;soak monitor 消费的进度文件。
run_summary.json 必须携带 metric schema 1 与内嵌 runtime-manifest
schema 1。manifest 记录有效 runtime limits、发生预算决策时的 inference /
tensor memory budget、collector/backend device、inference-flight 状态、
replay-ingress 状态、shutdown diagnostics,以及 backend tensor runtime 诊断
(runtime_manifest.backend_diagnostics;PPO run 以顶层
backend_diagnostics 携带同样的 payload)。字段与兼容契约见
日志。
soak JSON 是自包含 monitor evidence,artifact schema 为 0.3.0。当前 head
通过必须满足:
证据 |
通过值 |
|---|---|
Monitor 状态 |
|
Trainer 返回码 |
|
Run 状态 |
|
Failure reason 与 classification |
均为 |
最终 inference queue depth 与 publication lag |
均为 |
Replay 最终 occupancy |
|
Replay dropped batches 与 early returns |
均为 |
Replay release sequence |
等于 published sequence |
Published sequence |
|
Shutdown 分类 |
|
Shutdown cleanup 错误 |
|
Post-shutdown residual processes |
|
resource_summary 另外记录 process、RSS、GPU memory、eventfd、
shared-memory FD、replay high-water occupancy、inference event count 与最大
in-flight request 的峰值。出现峰值是正常的;需要关注的是 post-startup
样本中的无界或持续增长,而不是非零最大值本身。
artifact 的 context 记录 UniLab commit 与 dirty 状态、GPU UUID/名称/驱动、
Python/Torch 元数据与 CUDA_VISIBLE_DEVICES。JSON 与 run 目录需要一起
保留。仓库有意不跟踪这些机器生成 artifact。
运行后提取最后 20 个 schema-v1 TensorBoard 窗口:
uv run --no-sync scripts/benchmark/rl/extract_offpolicy_metrics.py \
/absolute/path/to/g1-flashsac-mjwarp-run \
--last 20 \
--json
helper 遇到缺失或不支持的 metric schema 会 fail closed。历史 unversioned
event 文件必须显式使用 --legacy-unversioned,永远不会作为隐式 fallback。
历史 Soak 证据¶
M11 lifecycle review 在本地 RTX 5090 上获得通过的 v6 长 soak:65,000 迭代、 66,660,352 环境 step、1987.55 秒、无 dropped replay batch、最终 replay occupancy 为 0、最终 inference queue/lag 为 0、normal shutdown 且无 residual process。资源采样显示 process、RSS、GPU memory、eventfd 与 shared-memory FD 均有界。
该 v6 artifact 是历史证据,不是当前 head 的 benchmark 声明。它使用 soak
artifact schema 0.2.0,且 UniLab、unilab-rl、UniSim commit 早于当前
schema-v1 consumer enforcement。需要当前 head 证据时,请用上面的命令在确切
checkout 上重新执行。
可选 Phase-Local Probe¶
g1_flashsac_backend.py 是诊断 probe,不是 canonical 端到端 benchmark。它
刻意排除 inference IPC、replay ingestion、learner update 与生产
Manager-Based dispatch。
uv run --no-sync scripts/benchmark/torch_env/g1_flashsac_backend.py \
--backends mjwarp,mujoco \
--num-envs 2048 \
--warmup 20 \
--iters 100 \
--acceptance \
--output scripts/benchmark/outputs/g1-flashsac-backend/current-host.json
acceptance 模式要求可查询 nvidia-smi,且每个隔离 backend 进程前后 GPU 均
空闲。它还会拒绝 Isaac worker profiler 环境变量。
JSON schema 为 0.3.0。跨 backend 比较使用
throughput_env_control_steps_per_s 与同步后的
phases.iteration_ms。单个 phase 边界可能按 stream 排序,因此不要把 phase
相加成新的墙钟总量,也不要把一个 backend 的 update_state_ms 直接与另一个
backend family 的同名 phase 比较。MuJoCo 结果还包含 packed H2D/D2H 清单与
transfer counter。
故障排查¶
现象 |
首要处理 |
|---|---|
MJWarp import 失败 |
重新执行显式 |
CUDA 不可用 |
检查 driver/container runtime 与 Torch sanity 命令;不要修改 task owner 强制 fallback。 |
物理 GPU 错误 |
在启动 |
CUDA IPC 或 worker 不匹配 |
保持 learner、collector 与 payload 在同一物理 GPU。外部 Isaac worker 继承可见性,但不继承 host Python path。 |
资产启动失败 |
预取 G1 资产,确认 motion NPZ 存在;首次下载完成前移除 |
startup timeout |
查看 |
stale progress |
打开最新 run 文件与 console log;超过 |
replay backpressure 或 drop |
读取 occupancy、high-water、wait、early return 与 dropped batch;未做 workload benchmark 前不要缩小 ingress slot rows。 |
最终 replay occupancy 非零 |
保留 artifact 与 console log;即使 trainer 看似完成,这也是 lifecycle/finalize 失败。 |
eventfd/shared-memory 增长 |
对比 post-startup 样本;有界峰值正常,持续增长是泄漏信号。 |
abnormal shutdown |
用 |
residual process |
保留记录已知 PID 与 start tick 的 artifact;在进程被解释并消失前不要复用 GPU。 |
unsupported capability |
阅读精确 fail-closed 错误与生成的 support matrix;CUDA-only lifecycle 没有 hidden NumPy 或 host-bridge fallback。 |
完整 schema 定义与 metric 判读见 日志 与 Tensor runtime。
Tensor Runtime 破坏性迁移¶
env.tensor_runtime 与 env.tensor_runtime_device 已移除,且没有兼容别名。
Manager runtime 构造上就是 tensor-native:设备位置由 backend 声明的数据面与
rank 进程设备推导。此前设置这两个字段的 owner YAML 和 runner override 必须
删除它们。不支持的 tensor lifecycle 会在 binding 阶段失败,不会回退到 NumPy
wire。