Work type: benchmark / perf Area: mujoco 规模预估: Small(选项 A:1–2 文件,≤50 行手写)
1. 一句话问题
单卡 sac + g1_motion_tracking + mujoco、algo.num_envs=4096 稳态训练时整机 CPU 占用只有 ~48%:MuJoCo BatchEnvPool 默认起 2×cpu_count 个 worker(本机 64 线程 / 16 物理核),物理相在 ~16–20 核即被内存带宽饱和,同时每个 collector cycle 约 1/3 墙钟花在只用 1–4 核的串行 host 相(update_state / reset_done / replay 写入 / 等 learner 动作),两者叠加把平均利用率结构性拉到 50% 以下。
2. 为什么现在做(实测证据)
机器:Xeon w5-3435X(16 物理 / 32 逻辑核)+ RTX 6000 Ada;commit f734cabe(基线分支 dev/issue-1304-motion-numba-body-state)。
复现:uv run train --algo sac --task g1_motion_tracking --sim mujoco algo.num_envs=4096(诊断时加 training.trace_enabled=true)。
稳态训练(400 iter,cycle 77.2ms,26540 steps/s):
| 证据 |
数值 |
| /proc/stat 整机 CPU |
47.5–49.2% |
| collector 进程 |
~14.3 核(102 线程) |
| learner 进程 |
~0.5 核;每 iter 等 collector 数据 43.2ms,自身更新 38.0ms(与 collector env step 重叠) |
| collector cycle 构成 |
env_step 72.7ms(94%)+ 等 learner 动作 7.7ms + replay 写入 2.0ms |
env.step 分相归因(独立进程,4096 envs,随机动作 → reset 偏重,150 步均值;绝对值与真实训练不同,看比例与核数):
| 配置 |
backend_step |
update_state |
reset_done |
整步 |
| 默认 64 线程 |
78.5ms @ 20.6 核 |
20.7ms @ 1.8 核 |
11.8ms @ 4.1 核 |
113.2ms @ 15.0 核 |
| 32 线程(cpu_ids=0–31) |
59.5ms @ 27.2 核 |
12.3ms @ 1.9 核 |
9.1ms @ 3.7 核 |
82.2ms @ 20.4 核,steps/s +38% |
pool 裸测(G1 scene_flat.xml,4096 envs × 3 substeps,无 env 语义):
| nthread |
64 默认不绑定 |
32 不绑定 |
32 pinned |
24 pinned |
16 pinned |
8 pinned |
| ms/step |
77.6 |
69.9 |
69.7 |
72.9 |
73.2 |
141.7 |
| 实占核 |
25.1 |
29.9 |
30.3 |
23.6 |
15.8 |
8.0 |
结论:
- 收益来自线程数而非绑定(32 不绑定 ≈ 32 pinned);64 线程比 32 慢 11%,超订纯亏。
- 物理吞吐在 ~16 物理核饱和(16→32 线程仅 +5% wall):CPU% 上不去首先是内存带宽瓶颈,其次是串行 host 相摊薄。追“100% CPU”不是本 issue 目标。
- 代码事实:
src/unilab/base/backend/mujoco/backend.py:355 默认 nthread = min(num_envs, 2*cpu_count)(沿用早期 rollout 惯例;曾经的 UNILAB_MUJOCO_STEP_THREADS 旋钮在 ab9614fa 被移除,未见调优依据);EnvCfg.cpu_ids 仅由多卡 DP 路径注入(src/unilab/ipc/dp_launcher.py:128 单卡返回 None),单卡用户没有配置入口。
诊断脚本(可复现以上各表):见评论区链接的分支 scripts/benchmark/env/ 下两个新脚本。
3. 最小交付结果
把单卡(cpu_ids=None)默认 pool 线程数从 2×cpu_count 收敛到有效 CPU 数(建议 len(os.sched_getaffinity(0)),尊重 taskset/cgroup),本机该 task 物理相预期提速 ~10%(独立 env 基准整步 +38%,真实训练受串行 host 相稀释会更低)。两个真实候选:
- A(推荐):改默认 sizing 一行 + 更新受影响单测。规模 Small,长期维护成本为零。
- B:扩展 chunk_tuner 做 (nthread, chunk_size) 联合 autotune(沿用
adaptive_chunk_size 的 cache 机制),逐机自适应;但要动 tuner,规模升 Standard,留作后续。
4. In scope
src/unilab/base/backend/mujoco/backend.py 默认 _n_threads sizing 规则(仅 cpu_ids is None 路径)。
- 受影响既有单测更新 + 一条 sizing 规则单测。
- 用复现命令在同机验证稳态 steps/s 与 CPU 占用前后对比。
5. Non-goals
6. Owner 与预计改动
- Owner layer:
src/unilab/base/backend/mujoco(backend 适配层)。
- 预计 1–2 文件、≤50 行手写(选项 A),1 个 PR;执行 owner 待 maintainer 指派。
7. Acceptance criteria
- 单卡默认
nthread == min(num_envs, 有效 CPU 数);显式 cpu_ids 时仍为 len(cpu_ids)。
make check 与受影响单测通过;chunk tuner 因 cache key 含 nthread 自动重调,无脏 cache。
- 同机复现命令稳态 steps/s 提升(预期 ≥5%),issue/PR 中记录前后对比数据。
8. Stop conditions
Work type: benchmark / perf Area: mujoco 规模预估: Small(选项 A:1–2 文件,≤50 行手写)
1. 一句话问题
单卡
sac + g1_motion_tracking + mujoco、algo.num_envs=4096稳态训练时整机 CPU 占用只有 ~48%:MuJoCo BatchEnvPool 默认起2×cpu_count个 worker(本机 64 线程 / 16 物理核),物理相在 ~16–20 核即被内存带宽饱和,同时每个 collector cycle 约 1/3 墙钟花在只用 1–4 核的串行 host 相(update_state / reset_done / replay 写入 / 等 learner 动作),两者叠加把平均利用率结构性拉到 50% 以下。2. 为什么现在做(实测证据)
机器:Xeon w5-3435X(16 物理 / 32 逻辑核)+ RTX 6000 Ada;commit
f734cabe(基线分支dev/issue-1304-motion-numba-body-state)。复现:
uv run train --algo sac --task g1_motion_tracking --sim mujoco algo.num_envs=4096(诊断时加training.trace_enabled=true)。稳态训练(400 iter,cycle 77.2ms,26540 steps/s):
env.step 分相归因(独立进程,4096 envs,随机动作 → reset 偏重,150 步均值;绝对值与真实训练不同,看比例与核数):
pool 裸测(G1 scene_flat.xml,4096 envs × 3 substeps,无 env 语义):
结论:
src/unilab/base/backend/mujoco/backend.py:355默认nthread = min(num_envs, 2*cpu_count)(沿用早期 rollout 惯例;曾经的UNILAB_MUJOCO_STEP_THREADS旋钮在ab9614fa被移除,未见调优依据);EnvCfg.cpu_ids仅由多卡 DP 路径注入(src/unilab/ipc/dp_launcher.py:128单卡返回 None),单卡用户没有配置入口。诊断脚本(可复现以上各表):见评论区链接的分支
scripts/benchmark/env/下两个新脚本。3. 最小交付结果
把单卡(
cpu_ids=None)默认 pool 线程数从2×cpu_count收敛到有效 CPU 数(建议len(os.sched_getaffinity(0)),尊重 taskset/cgroup),本机该 task 物理相预期提速 ~10%(独立 env 基准整步 +38%,真实训练受串行 host 相稀释会更低)。两个真实候选:adaptive_chunk_size的 cache 机制),逐机自适应;但要动 tuner,规模升 Standard,留作后续。4. In scope
src/unilab/base/backend/mujoco/backend.py默认_n_threadssizing 规则(仅cpu_ids is None路径)。5. Non-goals
6. Owner 与预计改动
src/unilab/base/backend/mujoco(backend 适配层)。7. Acceptance criteria
nthread == min(num_envs, 有效 CPU 数);显式cpu_ids时仍为len(cpu_ids)。make check与受影响单测通过;chunk tuner 因 cache key 含 nthread 自动重调,无脏 cache。8. Stop conditions