Skip to content

perf: end-to-end MJWarp training throughput gate and tensor-native closeout #2023

Closed

Description

@TATP-233

perf: end-to-end MJWarp training throughput gate and tensor-native closeout

Objective

Close the remaining #1811 runtime work using end-to-end training throughput as the decisive performance gate. The canonical production behavior is the real uv run train path—not a collector-only microbenchmark and not a final single iteration.

Canonical workloads

Both workloads are mandatory and run on MJWarp:

  1. FlashSAC / g1_motion_tracking
    uv run train \
      --algo flashsac \
      --task g1_motion_tracking \
      --sim mjwarp
  2. SAC / g1_walk_flat
    uv run --extra mjwarp train \
      --algo sac \
      --task g1_walk_flat \
      --sim mjwarp

Measurement contract

The acceptance metric is the new runner-owned:

run_summary.json:tail_env_steps_per_sec

from unilab-rl #74.

Definition:

sum(env steps represented by the final 20 completed training iterations)
/
sum(wall time of those iterations)

Rules:

  • It is an aggregate-rate over one bounded window, not the mean of per-iteration rates.
  • The window is 20 iterations: large enough to suppress single-iteration jitter, small enough not to extend development loops.
  • Startup, backend materialization, Torch compilation, graph capture, replay fill, and first-update warm-up are excluded.
  • Existing final_env_steps_per_sec is diagnostic only and is not acceptance evidence.
  • Collector scripts under scripts/benchmark are diagnostic attribution tools only. They must never replace the end-to-end train gate.

Current local feasibility evidence

Local RTX 4090, 160 iterations, 2048 envs, no playback/ONNX, CUDA_VISIBLE_DEVICES unset:

Workload tail steps/s final-iter steps/s
FlashSAC / g1_motion_tracking / MJWarp 104,652 104,797
SAC / g1_walk_flat / MJWarp 96,383 82,203

This proves the metric is feasible and demonstrates why the final iteration is unsuitable: SAC's final iteration reports 82k while its bounded tail is 96k.

Artifacts: /tmp/flashsac-tail-contract/run_summary.json and /tmp/sac-tail-contract/run_summary.json.

Acceptance targets

Local workstation

  • Local results establish feasibility and guide optimization.
  • FlashSAC motion already exceeds 100k steps/s.
  • SAC walk must reach ≥100,000 tail steps/s.

592 / RTX 5090 server

Both canonical workloads must reach:

tail_env_steps_per_sec >= 100,000

A performance claim requires repeated interleaved end-to-end training runs under the same contract. A single run is not acceptance evidence.

Backend and architecture scope

Active runtime scope:

  • mjwarp
  • mujoco

Shelved for this effort:

  • Genesis
  • all other adapters

Non-negotiable runtime invariants

The following must remain true after every change:

  1. Manager-Based API is the sole training entry point.
    • No direct environment mode.
    • No task-owned direct runtime registration.
  2. No rollback to a generic NumPy Manager.
  3. No rollback to a NumPy RNG owner.
    • The Manager-owned Torch RNG remains authoritative.
  4. No re-introduction of env.tensor_runtime / env.tensor_runtime_device.
    • Placement is derived from actual public env carrier placement and backend capabilities.
  5. No backend-private probing from task/env/training code.
    • Backend behavior must remain behind declared SimBackend public capabilities/APIs.
  6. No compatibility layer or legacy NumPy owner path for MJWarp.
    • Tensor-native is the only MJWarp execution path.
  7. NumPy is permitted only at the explicit MuJoCo HOST_BRIDGE D2H/H2D boundary.
    • MJWarp owners must not use NumPy state carriers, NumPy reset composers, or host RNG reset paths.

Required tensor-native migration

A. g1_walk_flat host-composer removal

This is the primary architectural blocker and the nearest local performance gap.

Remove the NumPy host owner paths from the canonical MJWarp SAC owner:

  • UniformVelocityCommand / G1VelocityCommand
    • remove NumPy command mirrors such as vel_command_b, vel_command_w, heading_target, heading_error;
    • remove NumPy is_heading_env, is_standing_env, is_world_env, is_forward_env masks;
    • remove selected-row D2H and _host_uniform reset sampling;
    • execute resampling, dead-zone filtering, and update using Torch rows and Manager Torch RNG.
  • G1GaitPhase.reset
    • remove selected-row D2H and NumPy RNG sampling;
    • sample phases on the public device using Manager Torch RNG.
  • Reset events
    • replace canonical reset_root_state_uniform and reset_scene_to_default usage with their tensor event peers;
    • remove the origins_tensor.abs().max().item() hot-path synchronization by making origin applicability a cold-path owner fact or otherwise device-resident branch.
  • G1PenaltyCurriculum / EpisodeLengthTracker
    • keep episode-length statistics and scaling device-resident during steady training;
    • convert to host scalar only at the unavoidable reward-weight mutation boundary, and avoid doing so on every reset.
  • G1 walk sensor/reward owners
    • remove capability fallbacks that silently select a NumPy host reader on MJWarp;
    • fail closed if a required sensor is absent from the public tensor read plan.

B. MJWarp motion owner residual cleanup

The FlashSAC motion owner is already largely tensor-native, but closeout must verify and, where needed, remove:

  • legacy MotionCommand base-class NumPy methods still reachable from an MJWarp owner;
  • capability fallbacks that select host readers on a DEVICE_RESIDENT backend;
  • remaining selected-reset NumPy row mirrors;
  • reward/log host publication outside the interval-controlled collector contract.

C. Legacy path removal

No dormant MJWarp NumPy fallback should remain:

  • remove unreachable NumPy execution branches for MJWarp owners rather than preserving them as compatibility;
  • do not add an owner-level tensor/NumPy execution switch;
  • do not preserve “legacy behavior” in config comments or runtime dispatch.

MuJoCo HOST_BRIDGE remains a production backend, so its public NumPy boundary is not a fallback and is not deleted.

Benchmark governance

scripts/benchmark must be updated to remain useful but subordinate to the end-to-end gate:

  • mirror the canonical two-workload set;
  • exclude warmup/compile;
  • report a bounded tail aggregate rate;
  • preserve detailed phase attribution for optimization decisions;
  • clearly label results as diagnostic, not acceptance evidence.

Any benchmark change must not lengthen the canonical loop merely to produce a more impressive number.

Required validations

Each child PR must run the smallest relevant checks, and the final head must run the full gate.

Minimum final gates:

make check
make test
make test-all

Mandatory end-to-end runs:

uv run train \
  --algo flashsac \
  --task g1_motion_tracking \
  --sim mjwarp \
  algo.max_iterations=160 \
  algo.num_envs=2048 \
  training.no_play=true \
  training.export_onnx=false

uv run --extra mjwarp train \
  --algo sac \
  --task g1_walk_flat \
  --sim mjwarp \
  algo.max_iterations=160 \
  algo.num_envs=2048 \
  training.no_play=true \
  training.export_onnx=false

Record for each run:

  • UniLab commit
  • unisim commit
  • unilab-rl commit
  • GPU and CUDA visibility
  • completed_iterations
  • total_env_steps
  • tail_env_steps_per_sec
  • tail_iteration_count
  • training_wall_time_sec
  • run artifact path

Governance

  • Integration branch: dev/issue-1811-tensor-manager.
  • Do not modify main.
  • Final integration target: develop/tensor-runtime.
  • No PyPI release.
  • Direct changes to local sibling repositories are authorized:
    • unisim
    • unilab_rl
  • Sibling changes require their own reviewable PR, tests, and linked evidence; they must not become hidden dependencies of a UniLab PR.
  • Maintain ADR-0012 and the support matrix as scope and behavior change.
  • Do not close this issue merely because local throughput improves. Server repeated end-to-end evidence and the tensor-native architecture boundary are both mandatory.

Success criteria

  • MJWarp tensor-native is the sole execution path for both canonical tasks.
  • No canonical MJWarp owner uses a NumPy host composer, host RNG reset path, or hidden NumPy state mirror.
  • Local SAC / g1_walk_flat / MJWarp reaches ≥100,000 tail steps/s.
  • 592 / RTX 5090 repeated runs show both canonical tasks at ≥100,000 tail steps/s.
  • Diagnostic benchmarks mirror the end-to-end contract and clearly remain non-acceptance tools.
  • make check, make test, and make test-all pass on the final head.
  • Both canonical 160-iteration training runs complete with normal shutdown and no residual collector process.
  • Manager-only factory, scoped backends, tensor-switch removal, Torch RNG owner, and no-direct-mode invariants remain intact.
  • Documentation, ADR-0012, and generated support matrix reflect the final runtime.

Part of #1811 closeout.

Activity

  1. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged tensor-native velocity command: #2024 — perf(velocity): make command owner tensor-native (head b200ed24; integration merge 990cbb01).

    Change

    UniformVelocityCommand no longer owns NumPy state mirrors:

    • removed vel_command_b, vel_command_w, heading_target, heading_error;
    • removed NumPy is_heading_env, is_standing_env, is_world_env, is_forward_env;
    • removed selected-row D2H, _host_uniform, NumPy mask indexing, and full-width NumPy→Torch publication.

    It now owns Torch command/world/heading/mask carriers and uses device int64 rows. With the Manager Torch RNG, resampling and mask sampling are fully device-resident. RoughVelocityCommand also loses its legacy NumPy fallback.

    The only remaining NumPy RNG branch is the explicit CPU/HOST_BRIDGE reference fallback when env.torch_rng is absent; it uploads once directly to the owner carrier and does not keep a hidden NumPy owner mirror.

    Correctness

    • Uniform velocity command tests: 12 passed.
    • Velocity/G1/Manager-env/gait/motion partial-reset suites: 116 passed.
    • make check: passed.
    • The two slow G1 penalty-curriculum failures are pre-existing on the unmodified integration head, verified by stashing this change.

    Local end-to-end tail-throughput result

    Both canonical workloads, 160 iterations, 2048 envs, no playback/ONNX, CUDA mask unset:

    Workload Before #2024 After #2024
    FlashSAC / g1_motion_tracking / MJWarp 104,652 113,710
    SAC / g1_walk_flat / MJWarp 96,383 101,286

    Artifacts:

    • /tmp/flashsac-tensor-command-check/run_summary.json
    • /tmp/sac-tensor-command/run_summary.json

    Both canonical local gates now exceed the 100,000 tail_env_steps_per_sec target. This is a single local run, not server acceptance evidence; repeated 592 runs are still required.

  2. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged tensor-native G1 walk reset events: #2025 — perf(walk): select tensor reset events on mjwarp (head 645e47ec; integration merge 3079216c).

    Change

    All G1 walk MJWarp owners now select the tensor reset events:

    reset_scene_to_default_tensor
    reset_root_state_uniform_tensor
    

    Covered owners: SAC, FlashSAC, PPO, WarpSAC.

    The tensor root event's immutable-origin applicability is now resolved once on cold-path origin-table materialization and cached as a boolean. The prior hot-path origins_tensor.abs().max().item() synchronization is removed. MuJoCo retains its explicit HOST_BRIDGE NumPy reset boundary.

    Correctness

    • G1 owner materialization contracts: 8 passed.
    • Real CUDA selected tensor-root event: 1 passed.
    • make check: passed.

    Local end-to-end gate

    SAC / g1_walk_flat / MJWarp, 160 iterations, 2048 envs:

    Head tail steps/s
    #2024 101,286
    #2025 102,117

    Artifact: /tmp/sac-tensor-reset-events/run_summary.json.

    The local gate remains above 100k. No server or repeated-run claim is made.

  3. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged tensor-native G1 gait phase reset: #2026 — perf(gait): make phase reset tensor-native (head 0988f13c; integration merge 8a77a5663).

    Change

    The shared G1 gait phase carrier is now always a Torch tensor on the environment device. Reset uses device int64 rows, Manager Torch RNG sampling, and direct index_copy_; it no longer converts selected rows to NumPy or uploads host-built phase samples.

    The explicit CPU/HOST_BRIDGE reference fallback remains for owners without Torch RNG and uploads selected rows once to the public phase carrier. HOST_BRIDGE gait rewards convert shared Torch phase targets once at their public NumPy sensor boundary.

    Correctness

    • Gait phase focused tests: 2 passed.
    • G1 manager-term slow suite excluding two pre-existing penalty-curriculum failures: 10 passed.
    • make check: passed.

    Local end-to-end gate

    SAC / g1_walk_flat / MJWarp, 160 iterations, 2048 envs:

    Head tail steps/s
    #2025 102,117
    #2026 100,177

    The variation is within the single-run local band and remains above the 100k target. No server or repeated-run claim is made.

  4. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged tensor-native G1 penalty curriculum: #2027 — perf(curriculum): track episode lengths on device (head cdef9765; integration merge e4b0aa5c).

    Change

    EpisodeLengthTracker now stores its recursive weighted average as a float64 Torch scalar on the environment device. G1PenaltyCurriculum selects done rows on device, updates without per-reset D2H, removes the NumPy episode-length fallback, and publishes one host scalar per curriculum update. That publication is required because reward weights remain public Python floats.

    Correctness

    • Full G1 manager-term slow suite: 12 passed. This also fixes the two curriculum tests that had been failing on prior integration heads.
    • make check: passed.

    Local end-to-end gate

    SAC / g1_walk_flat / MJWarp, 160 iterations, 2048 envs:

    Head tail steps/s
    #2026 100,177
    #2027 101,286

    Artifact: /tmp/sac-tensor-curriculum/run_summary.json.

    No server or repeated-run performance claim is made.

  5. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged DEVICE_RESIDENT sensor fail-closed negotiation: #2028 — fix(env): fail closed on missing device tensor sensors (head 15a4931f; integration merge 027c29f4).

    Change

    Declared tensor_sensor_names on observation, command, reward, and termination terms are now mandatory on DEVICE_RESIDENT backends. Names absent from the audited public tensor-sensor set fail at read-plan construction with an explicit NotImplementedError naming backend, missing sensors, manager, and term.

    Previously unavailable names could be filtered from the read plan, after which a term's hot path silently selected its host/cache reader. That hidden NumPy fallback is removed for MJWarp and other DEVICE_RESIDENT runtimes. HOST_BRIDGE negotiation remains explicit and unchanged.

    Correctness

    • Manager env + G1 owner slow suites excluding two pre-existing Genesis runtime smokes: 16 passed.
    • The two Genesis failures were reproduced on the unmodified integration head.
    • make check: passed.
    • Local SAC / g1_walk_flat / MJWarp completed normally at 99,638 tail steps/s, confirming all canonical task sensor declarations are satisfied.

    No server performance claim is made.

  6. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged diagnostic benchmark tail throughput: #2029 — perf(benchmark): report diagnostic tail throughput (head 49288cf6; integration merge 613c21d8).

    Change

    scripts/benchmark/rl/benchmark_offpolicy_collector_active.py now reports an aggregate bounded-tail rate:

    tail_active_steps_per_sec =
    num_envs * tail_step_count / sum(active time in tail window)
    

    Controls/outputs:

    • --tail-steps (default 20, clamped to measure steps);
    • tail_step_count;
    • tail_active_steps_per_sec;
    • console tail/s=<rate>(<count>);
    • JSON args and CSV columns.

    This mirrors the runner-owned aggregate-tail definition while remaining explicitly diagnostic. It does not replace end-to-end training tail_env_steps_per_sec as the acceptance gate.

    Correctness

    • Benchmark contract tests: 27 passed.
    • make check: passed.

    Local diagnostic run

    SAC / g1_walk_flat / MJWarp, 2048 envs, warmup 10 / measure 100 / tail 20:

    Metric Value
    whole-window collector rate 167,382/s
    tail aggregate rate 162,478/s

    Artifact: /tmp/bench-tail2/results.{json,csv}.

  7. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged G1 walk fused tensor reset owner: #2030 — perf(walk): fuse G1 tensor reset owner (head aae38031; integration merge 70b2a50e).

    Change

    The MJWarp SAC G1 walk owner now uses a Manager-owned G1WalkResetOwner:

    • command reset consumes reset_command_state with publish_metrics=false;
    • bounded Torch sampling skips the redundant generic full-command finite synchronization;
    • action/metric reset uses existing selected-row clear boundaries;
    • construction fails closed unless the term is UniformVelocityCommand and the Manager Torch RNG is present.

    No NumPy owner, host RNG reset path, compatibility layer, or backend-private probe was added.

    Correctness

    • New G1 reset-owner contract tests: 2 passed.
    • Velocity command and motion owner config focused tests: 20 passed.
    • make check: passed.
    • SAC/MJWarp 20×64 smoke completed normally.
    • SAC/MJWarp 160×2048 completed at 101,493.86 tail env steps/s.

    Diagnostic reset attribution (local 4090; non-acceptance evidence)

    Metric #2029 #2030
    collector whole-window 167,382/s 168,617/s
    collector bounded tail 162,478/s 170,212/s
    reset_done_ms mean 7.69 ms 3.09 ms
    manager reset passes 6.88 3.93
    command metric publication 0.280 ms 0.036 ms
    command resample 0.822 ms 0.257 ms

    Server baseline

    Before #2030, repeated 592/RTX 5090 160×2048 end-to-end runs under the new contract completed at:

    • SAC / walk: 65,715–65,900 tail steps/s (3 runs)
    • FlashSAC / motion: 68,394–71,274 tail steps/s (3 runs)

    Commits: UniLab 613c21d8, unisim 2eb70e9, unilab-rl 72a50b3; CUDA_VISIBLE_DEVICES=0.

  8. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    592/RTX 5090 result after #2030

    UniLab 70b2a50e, unisim 2eb70e9, unilab-rl 72a50b3, CUDA_VISIBLE_DEVICES=0, 160 iterations × 2048 envs.

    SAC / g1_walk_flat / MJWarp completed at 66,527.91 tail env steps/s (baseline band 65,715–65,900).

    The PR improves local fast-host reset dispatch substantially but does not close the server gap. Diagnostic collector attribution on the same server shows:

    Metric #2029 #2030
    collector whole-window 102,832/s 106,708/s
    collector bounded tail 101,937/s 106,267/s
    reset_done_ms 7.69 ms 7.09 ms
    manager reset passes 6.88 3.97

    The remaining server reset time is not primarily generic Manager dispatch:

    • event + reset commit: ~5.30 ms;
    • reset observation rebuild: ~1.12 ms;
    • command resampling: ~0.82 ms;
    • backend selected set_state: ~1.31 ms.

    The much larger end-to-end deficit is the learner/collector overlap profile: training remains around 66.5k while the collector-only hot loop is around 106k. The next required attribution is therefore the end-to-end iteration phase breakdown on 592, not another generic Manager micro-optimization.

  9. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    End-to-end 592 phase attribution and #2031

    Attribution method

    Analyzed the runner TensorBoard event files from the three repeated 592/RTX 5090 baseline runs and compared them with local RTX 4090 runs under the same head and workload.

    Server-specific SAC bottleneck

    Tail mean, SAC / g1_walk_flat / MJWarp:

    Phase local 4090 592 Xeon 8380
    action validation 0.061 ms 0.101 ms
    apply action 0.479 ms 10.9–11.1 ms
    backend step 10.864 ms 4.67–5.33 ms
    update state 2.886 ms 6.12–6.30 ms
    reset done 3.905 ms 7.09–7.25 ms
    inference wait 1.681 ms 3.89–4.17 ms
    env step total 18.266 ms 29.3–29.9 ms

    The server CPU is not uniformly slow: it is substantially faster in backend wall time, but the full-width indexed control publication costs ~11 ms there versus ~0.5 ms locally.

    Fix

    #2031 perf(action): bypass indexed full-width control write (head 579aee43; integration merge c40b5e21).

    Entity._write_tensor_ctrl now recognizes the exact full-width case—all rows, all configured entity actuators, actuator order unchanged—and publishes through one contiguous control.copy_(values). Partial/reordered writes retain the validated indexed path. Actuator IDs are cached once per device.

    Results

    Local RTX 4090, 160×2048:

    Head tail steps/s
    #2030 101,493.86
    #2031 103,605.46

    592/RTX 5090, 160×2048:

    Head tail steps/s
    #2030 66,527.91
    #2031 66,926.50

    The server apply_action telemetry did not improve as expected, so #2031 is retained as a correct full-width fast path but is not yet the decisive server fix. The remaining 592 SAC tail profile is approximately:

    • apply action 11.19 ms;
    • update state 6.12 ms;
    • reset done 7.12 ms;
    • backend step 4.67 ms;
    • action wait 3.89 ms.

    Next attribution target is the call graph inside apply_action: action-manager finite validation/history copies versus Entity.set_joint_position_target and control publication.

  10. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Refined 592 action-path attribution

    Isolated CUDA action-operation benchmark on 592, RTX 5090, 2048×29 tensors, 1000 iterations after warmup:

    Operation wall for 1000 calls
    one isfinite(...).all() host check 54.36 ms
    raw action copy 6.49 ms
    affine copy 6.52 ms
    encoder-bias index_select 13.39 ms
    subtract 6.24 ms
    contiguous control copy 6.50 ms
    representative five-op action sequence including finite check 102.13 ms

    This shows why apply_action remains ~11 ms per step on 592 and ~0.5 ms locally: the slow-frequency Xeon host pays roughly one CUDA launch/synchronization tax per small kernel and the canonical action path performs several small full-batch kernels plus host finite checks. The indexed control scatter was only one contributor; #2031's contiguous copy remains valid but cannot remove the other launch and synchronization points.

    The next optimization must reduce the number of separate action kernels/validations, not optimize one indexed write in isolation. Candidate direction is a fused tensor action boundary for the canonical full-width joint-position action:

    1. avoid duplicate finite validation already performed by ActionManager.process_action and _write_tensor_ctrl;
    2. avoid the materialized encoder-bias index_select when the target order is contiguous;
    3. fuse affine transform, encoder-bias subtraction, and control publication;
    4. retain the generic indexed path for partial/reordered/noncanonical actions.

    This requires a narrowly scoped owner change and explicit tests proving validation remains fail-closed.

  11. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Current execution head: c40b5e21 (#2030 + #2031). Remaining mandatory closeout: fused action-boundary optimization/re-attribution, Genesis scope reduction, final full test gate, repeated server acceptance runs, docs/ADR/support-matrix closeout.

  12. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged device-resident encoder bias: #2032 — perf(action): keep device encoder bias resident (head bb414129; integration merge e3dea302).

    Change

    EntityData.encoder_bias_tensor now owns a persistent device-resident carrier when the entity has a Torch control plane. RandomizeEncoderBias samples selected rows/joints directly into that carrier using the Manager Torch RNG and no D2H. CPU/HOST_BRIDGE owners retain the existing NumPy carrier.

    Local validation

    • make check — passed
    • joint-position action + event focused tests — 73 passed
    • FlashSAC / motion, 160×2048 — 110,923.99 tail steps/s
    • SAC / walk, 160×2048 — 102,602.46 tail steps/s

    592/RTX 5090 repeated runs after #2032

    Workload runs tail steps/s
    SAC / g1_walk_flat 2 66,750.61; 66,889.02
    FlashSAC / g1_motion_tracking 2 70,105.86; 71,150.99

    Tail phase means remain:

    • SAC: apply action 11.12 ms, update 6.09 ms, reset 7.06 ms, backend 4.86 ms, wait 4.14 ms.
    • FlashSAC: apply action 1.27 ms, update 7.98 ms, reset 8.24 ms, backend 7.98 ms, wait 7.08 ms.

    Important attribution correction: the canonical g1_walk_flat owner does not configure RandomizeEncoderBias, and its JointPositionAction still performs an encoder-bias index_select against the device carrier. The canonical FlashSAC motion owner does not configure the reset event either, but benefits from removing the per-action temporary .to(device) conversion. This explains the local/motion gain and the negligible SAC server gain.

    Next server-side targets remain SAC action-path kernel-count reduction and reset/update-state dispatch reduction; no server performance claim is made from #2032.

  13. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged single action finite boundary: #2033 — perf(action): remove duplicate finite synchronization (head e67b9a2b; integration merge 014d9b34).

    Change

    ActionManager.process_action remains the authoritative whole-action finite check. Affine action owners expose validate_actions() and no longer perform a second full-tensor host synchronization on their own slices. The motion latency owner validates the raw input actually used after latency adjustment.

    Validation

    • make check — passed
    • action/motion focused tests — 68 passed
    • local SAC / walk, 160×2048 — 104,280.15 tail steps/s

    592 result

    A single SAC / walk run after #2033 completed at 66,662.68 tail steps/s. This confirms the remaining ~11 ms server apply_action cost is dominated by multiple small CUDA launches (history copies, affine, bias selection, subtraction, control copy), not merely duplicate finite synchronization.

    Current integration head is 014d9b34 (#2030–#2033). Server tail remains below the 100k gate; fused action publication remains the next candidate optimization.

  14. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Execution checkpoint: integration head 014d9b34 contains #2030–#2033. Local gates pass for both workloads; repeated 592 gate remains below target (SAC ~66.7k, FlashSAC ~70.1–71.2k). Remaining closeout: fused canonical action publication, reset/update-state kernel-count reduction, Genesis scope reduction, final full test gate, repeated server acceptance, docs/ADR/support matrix.

  15. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged full-width joint target publication: #2034 — perf(action): fuse full-width joint target publication (head e464113a; integration merge e61eb38a).

    Change

    Entity.set_full_width_joint_position_target is an explicit all-joints/all-rows Torch target boundary. It validates the immutable all-joint/one-actuator mapping once and writes directly into the control plane, replacing the generic path's actuator mapping, index-tensor reconstruction, equality check, finite reduction, and indexed scatter. JointPositionAction selects it only for the exact natural full-joint order.

    Validation

    • make check — passed
    • action/entity focused tests — 62 passed, 2 skipped
    • SAC/MJWarp smoke — completed normally
    • local SAC / walk, 160×2048 — 103,229.74 tail steps/s

    592 result

    SAC / walk after #2034: 66,264.16 tail env steps/s.

    Tail phase means changed as expected for the targeted path:

    Phase #2033 #2034
    apply action 11.12 ms 8.30 ms
    backend step 4.86 ms 7.53 ms*
    update state 6.09 ms 6.26 ms
    reset done 7.06 ms 7.21 ms

    *The backend-step shift shows the CPU-side action phase previously overlapped queued backend work; reducing its wall time exposes more backend launch time rather than producing an equal end-to-end saving.

    This confirms the launch-count reduction is real, but also that per-control-step kernel-count reduction alone cannot recover the server gate. The next necessary direction is batching multiple control steps per collector/learner coordination boundary (currently env_steps_per_sync=1) or reducing update/reset launch counts equivalently.

  16. 21 remaining items

  17. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged MJWarp duplicate control barrier removal: unisim #336 — perf(mjwarp): remove duplicate control barrier (head e8c5249; merge 9e2119e).

    Change

    MjwarpBackend.step_tensor previously:

    1. copied control on the current Torch CUDA stream;
    2. synchronized that stream;
    3. launched Warp physics;
    4. called device-wide synchronize_device().

    The first explicit stream barrier was redundant because the caller owns the public action tensor and cannot mutate it until step_tensor returns. The copy and physics launches now share one ordered Torch CUDA stream, and the method waits once on that stream.

    The public contract remains synchronous return-after-completion; only duplicate/barrier scope is removed.

    Validation

    • focused MJWarp backend tests — 21 passed
    • local SAC / walk — 100,208.09 tail steps/s
    • 592 SAC / walk with UniLab c4231907 — 65,508.07 tail steps/s, within the existing ~63–65k band

    No server performance claim is made; this is another launch/barrier-count reduction that the larger per-step lifecycle dominates.

  18. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Execution checkpoint: UniLab c4231907, unisim 9e2119e, unilab-rl 036fcba. Server SAC remains ~65.5k after removing the duplicate MJWarp control barrier. The bottleneck is confirmed structural per-control-step lifecycle dispatch, not a remaining micro-barrier.

  19. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged G1 walk termination pack: #2041 — perf(gait): fuse G1 walk termination pack (head 49d0ce00; integration merge 702d1795).

    Change

    G1WalkTerminationPack fuses the canonical tilt and base-height failure terms into one public tensor read:

    • upvector_z < cos(max_tilt_rad) is algebraically equivalent to acos(upvector_z) > max_tilt_rad;
    • root height uses the packed public body view;
    • timeout remains a separate Manager semantic;
    • MJWarp SAC owner has no fallback and fails closed when public sensor/body views are absent.

    Validation

    • make check — passed
    • focused G1/manager suites — 121 passed
    • local SAC / walk — 101,558.33 tail steps/s

    592 result

    SAC / walk: 65,395.94 tail steps/s. Env-step and update-state means are essentially unchanged from the preceding run; the small FPS shift is within run variation. The fused term is structurally correct but does not materially move the server ceiling.

  20. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Execution checkpoint: UniLab 702d1795, unisim 9e2119e, unilab-rl 036fcba. Termination pack merged; server SAC remains ~65.4k. Full repo gates remain clean on c4231907; final-head make test-all must be rerun after #2041 before closeout.

  21. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Rejected experiment: G1 walk observation pack

    I prototyped an actor-side G1WalkObservationPack analogous to MotionObservationPack, replacing the seven canonical actor terms with one concatenated carrier. The work was stopped and the branch deleted before PR; no integration head was affected.

    Reasons:

    1. ObservationManager cold-probes each term before the backend read plan exists. A new owner needs a shape-only zero fallback, unlike the already-supported motion owner pattern.
    2. The canonical actor policy observation width is 100, not 98/102. The pack had a semantic width mismatch and preserving noisy actor-only joint-pos/joint-vel corruption inside a fused owner would duplicate the Manager noise pipeline or change RNG/noise semantics.
    3. The pack would also need a critic variant to avoid divergent actor/critic carrier implementations, significantly expanding scope.
    4. Projected savings are bounded: reset observation is ~1.12 ms and per-step observation ~0.88 ms on 592; the reset path rebuilds only selected rows and the normal path is already tensor-native.

    This is not a safe #2023 closeout change under the current scope. It should be a separate owner-contract issue if pursued.

  22. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Final-head repository gate rerun after #2041

    UniLab 702d1795, unilab-rl 036fcba, unisim 9e2119e:

    make test-all
    1,864 passed, 20 skipped, 557 deselected
    benchmark smoke imports: 35/35 modules, 36/36 scripts
    

    make check is included and passes. Repository gates remain clean on the current integration head.

    Rejected experiment: actor-side G1WalkObservationPack was prototyped and discarded without a PR/merge. The Manager cold-probe/read-plan ordering, exact policy width (100), actor-only noise semantics, and need for a critic peer make it a separate owner-contract scope rather than a safe #2023 patch.

    Remaining unresolved acceptance item: repeated 592 end-to-end throughput is still below 100k.

  23. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Current status summary: architecture, tensor-native boundary, scope/docs, and full repository gates are complete on UniLab 702d1795 + unisim 9e2119e + unilab-rl 036fcba. The only unresolved #2023 acceptance blocker is repeated 592 throughput (SAC ~65k, FlashSAC ~70k), which evidence shows requires a new multi-step/lifecycle-batching scope rather than further single-owner micro-optimization.

  24. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Merged non-synchronizing tensor root validation: #2042 — perf(reset): validate tensor root payload without host sync (head d308da82; integration merge 82973d5e).

    Change

    The ordinary tensor root reset previously forced two Python bool conversions (finite reduction plus quaternion-norm reduction) on every selected reset. It now combines both predicates into one device boolean and compares it with torch.equal against a same-device true scalar, matching the existing fused motion-write success-path pattern.

    Invalid payloads still synchronize and retain the exact existing NaN/quaternion diagnostics.

    Validation

    • make check — passed
    • reset-state, real CUDA selected root reset, and root kernel suites — 53 passed
    • local SAC / walk, 160×2048 — 104,680.49 tail steps/s

    592 result

    SAC / walk: 65,497.80 tail steps/s, within the established ~65k band. The removed host sync is correct launch/synchronization reduction but does not overcome the per-control-step lifecycle ceiling.

  25. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Final-head repository gate after #2042

    UniLab 82973d5e, unisim 9e2119e, unilab-rl 036fcba:

    make test-all
    1,864 passed, 20 skipped, 557 deselected
    benchmark smoke imports: 35/35 modules, 36/36 scripts
    

    All repository quality gates and tensor-native architecture/scope/docs criteria remain satisfied. The unresolved acceptance blocker is still repeated 592 throughput below 100k.

  26. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Execution checkpoint: UniLab 82973d5e (#2030–#2042), unisim 9e2119e (#334–#336), unilab-rl 036fcba (#73–#75). Repository gates pass. Server SAC remains ~65.5k; only throughput acceptance is unresolved.

  27. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Final repeated 592 acceptance audit (authoritative closeout evidence)

    Commits: UniLab 82973d5e, unisim 9e2119e, unilab-rl 036fcba.
    GPU: RTX 5090, CUDA_VISIBLE_DEVICES=0. 160 iterations × 2048 envs, no play/ONNX. Runs interleaved in three full SAC/FlashSAC blocks.

    Workload tail env steps/s wall time status
    SAC / walk / run 1 66,528.84 7.51 s completed
    SAC / walk / run 2 64,527.07 8.16 s completed
    SAC / walk / run 3 63,929.63 8.08 s completed
    FlashSAC / motion / run 1 69,349.00 7.99 s completed
    FlashSAC / motion / run 2 70,972.11 7.89 s completed
    FlashSAC / motion / run 3 69,613.34 7.93 s completed

    All runs completed 160/160 iterations. Post-run process/GPU-compute inspection found no residual collector/training process.

    Acceptance result

    • Required: both workloads ≥100,000 tail env steps/s.
    • Observed: SAC 63.9k–66.5k; FlashSAC 69.3k–71.0k.
    • 592 acceptance gate is not met.

    Final attribution summary

    The server deficit is not explained by NumPy owners, host RNG, command/reward/termination per-term dispatch, action indexing, encoder-bias host mirrors, duplicate finite/control barriers, reset table caching, ring capacity, collector thread count, CPU/NUMA affinity, or a single missing owner kernel. Each was measured and, where structurally sound, fixed/merged (#2030–#2042, unisim #334–#336, unilab-rl #73–#75). Repeated results remain ~65–71k.

    The remaining cost is the per-control-step environment lifecycle itself: action, backend step, update-state, reset, observation rebuild, and learner/collector coordination are each individually small but collectively launch/dispatch-bound on the slow-frequency Xeon host. Closing the remaining gap requires a new public multi-step/lifecycle-batching or graph-execution scope, not another single-owner patch.

    Everything else in #2023 is complete on this head:

    • tensor-native MJWarp sole path and architecture invariants;
    • local ≥100k for both canonical workloads;
    • benchmark contract marked diagnostic-only;
    • Genesis shelved and matrices/docs updated;
    • make test-all: 1,864 passed, 20 skipped; benchmark imports 35/35 and 36/36;
    • canonical runs complete normally with no residual process.

    Issue #2023 should remain open until a successor scope delivers the required server throughput mechanism; it cannot be truthfully closed as passing its stated server acceptance gate.

  28. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Successor scope opened: #2043 perf: public multi-step MJWarp lifecycle execution for slow-host servers. It preserves the #2023 measurement contract and invariants and covers the remaining launch/dispatch-bound lifecycle gap that blocks the server throughput gate.

  29. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Development branch convergence status

    Current heads

    Repo Branch Commit State
    UniLab dev/issue-1811-tensor-manager 82973d5e clean, synced with origin
    unisim roadmap/issue-1811-tensor-manager 9e2119e clean, synced with origin
    unilab-rl develop/tensor-runtime 036fcba clean, synced with origin

    UniLab integration is 453 commits ahead of its declared final target develop/tensor-runtime and has no unique target-side commits. No PR to the final target has been opened yet.

    Convergence cleanup performed

    • Deleted 91 local UniLab feature branches whose heads were ancestors of 82973d5e.
    • Deleted 8 merged local unisim feature branches.
    • Deleted 6 merged local unilab-rl feature branches.
    • Corrected the unisim roadmap branch upstream to origin/roadmap/issue-1811-tensor-manager.
    • Pruned stale origin/fork remote-tracking refs.
    • Deleted stale fork feature branches whose heads were already ancestors of the integration head.
    • Removed the stale duplicate tensor remote from unisim.
    • Removed the local/remote residual test/genesis-torch-global-isolation branch. Its PR test(genesis): isolate torch global mutation #2011 was closed unmerged; its test passes on the current head without that patch and Genesis is now shelved, so no action is pending.

    Remaining local branches

    UniLab:

    • dev/issue-1811-tensor-manager — current integration
    • develop/tensor-runtime — declared final target
    • main — untouched long-lived base

    unisim:

    • roadmap/issue-1811-tensor-manager
    • unrelated dev/ray-query
    • main

    unilab-rl:

    • develop/tensor-runtime
    • unrelated fix/capture-compile (local-only)
    • main
    • release/1.4.4

    Open work

  30. TATP-233 commented on Oct 3, 2026

    @TATP-233
    CollaboratorAuthor

    Maintainer decision accepts the measured 592/RTX 5090 throughput result for the completed tensor-native runtime boundary. The integration branch was merged into the declared tensor-runtime target by #2044 (merge commit f1ce4399).\n\nCompleted: tensor-native MJWarp sole path, architecture invariants, scope/docs/support matrix, benchmark contract, full repository gates, normal canonical-run shutdown, and local ≥100k targets.\n\nNot met: original 100k server throughput gate. Repeated final-head results were SAC 63,930–66,529 and FlashSAC 69,349–70,972. The remaining launch/dispatch-bound lifecycle work continues under #2043 using the same measurement contract.\n\nSibling heads: unisim 9e2119e, unilab-rl 036fcba. No changes were made to main; no PyPI release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions