Tensor runtime¶
SAC and FlashSAC resolve their spawn-facing tensor-runtime settings
before probing the environment or constructing a learner. The owner YAML values
are collected into one bounded RuntimeSettings object and passed to the
off-policy runner. Invalid values, unsupported bounds, and impossible memory
combinations therefore fail before collector or learner processes are launched.
This contract covers the scoped single-GPU off-policy runtime. It is not an autotuner and does not add multi-GPU scaling.
Owner defaults and bounds¶
Setting |
Default |
Valid range or constraint |
|---|---|---|
|
|
Positive integer, at most |
|
|
Positive integer, at most |
|
|
Positive integer, at most |
|
|
|
|
|
|
Learner rows per synchronization |
|
Constrained by the CUDA memory budget |
Values other than training.cuda_process_sharing must be exact positive
integers. Booleans, strings, floating-point numbers, zero, and negative values
fail closed. training.cuda_process_sharing accepts only null or the explicit
string mps. The G1 Motion Tracking / MJWarp owner intentionally overrides only
collector_metrics_interval to 100; all other tensor-runtime defaults remain
as listed above.
Device semantics and resolution¶
Device-related settings are separated by the layer that consumes them:
Configuration key |
Effective layer |
Valid values |
|---|---|---|
|
Training assembly; decides collector-tensor residency (HOST_BRIDGE Manager carriers and the derived off-policy inference ring) |
|
|
Manager public tensors for a HOST_BRIDGE backend |
|
|
Learner and rank-local process selection |
Concrete Torch device label |
The training key is the only user-facing collector-tensor residency request.
auto follows the resolved learner device: CUDA routes the rank-local indexed
device, CPU routes CPU, and torch.mps (Metal Performance Shaders) retains the
owner/default CPU placement. Explicit cpu or cuda wins over auto. cuda
is concrete and never maps to MPS; a CUDA request on a host without CUDA fails
closed.
Carrier routing is capability-driven: MuJoCo, Motrix, Drake, and SuperDex
declare in-process packed HOST_BRIDGE transfer plans with cpu/cuda
carriers. Other backends ignore auto/cpu, but a cuda request fails
closed. A request outside the selected backend’s declared torch_devices
also fails closed. run_config.json retains the composed configuration and
records the configured-to-resolved mapping in the collector_tensor entry of
run.resolved_devices; startup logs print the same mapping.
The off-policy inference transport is a pure derived fact, not a configuration
key: collector_tensor_device=cpu derives the host ring (observations and
actions use the CPU inference ring; in the default-legal pairings the learner
itself is CPU or Apple MPS — see the pairing contract below for CUDA
learners), and cuda derives the CUDA ring. cuda follows Torch device-type
semantics and covers both NVIDIA CUDA and AMD ROCm. It does not relax separate
platform gates: CUDA-only tensor backends still fail closed on ROCm/HIP, and
training.cuda_process_sharing remains available only to the NVIDIA-specific
CUDA MPS daemon and native H2D implementation.
Removed keys fail closed with a migration error: use
training.collector_tensor_device instead of training.sim_carrier_device
and training.inference_transport, env.collector_tensor_device instead of
env.sim_carrier_device and manager_torch_device, and the resolved learner
device instead of env.tensor_runtime_device.
Migration note: a CUDA learner with the default auto presents CUDA tensors
through the packed host bridge and derives the CUDA inference ring. Each
collector process then holds one additional CUDA context on the learner GPU
(see Tensor Runtime Production Guide).
Pairing contract (issue #2483, ADR-0019): on a non-ROCm Torch build, an
explicit training.collector_tensor_device=cpu paired with a CUDA learner
fails closed — the CPU carrier is no longer a CUDA-learner performance path
(the #2479 retest showed throughput parity with CUDA carriers plus CUDA MPS).
CPU carriers remain legal for CPU learners, torch.mps learners (the only host
bridge topology on Apple Silicon), and ROCm/HIP builds (no CUDA MPS
equivalent; the CPU carrier is the only process-sharing mitigation).
Attribution baselines and debug runs retain the old topology by opting in
with UNILAB_ALLOW_CUDA_LEARNER_CPU_CARRIER=1.
The word “worker” is likewise layer-specific:
Term |
Meaning |
|---|---|
Collector worker |
APPO or off-policy process that steps environments and publishes rollouts |
Backend worker |
Backend-owned external simulator process (for example Isaac) |
torchrun rank |
Distributed learner rank launched by the PPO multi-GPU launcher |
Backend process device |
Process-global accelerator binding, not a Manager carrier key |
Replay ingress tradeoffs¶
The default replay_ingress_slot_rows: null resolves to algo.num_envs and
publishes one ingress chunk per collector vector. This keeps the publication
and synchronization pattern predictable and is the recommended production
default.
Setting the field to a smaller value reduces resident ingress memory by splitting one collector vector into contiguous chunks. It is intended only for explicit memory-pressure tuning. More chunks mean more publications, more opportunities for producer backpressure, and—on CUDA—one current-stream barrier per published chunk. Always benchmark a smaller value on the target workload before adopting it.
Ingress diagnostics count publication chunks, not complete logical collector vectors. A shutdown may retain a valid row prefix of a partially published vector; it never publishes an invalid transition row. See Logging for the replay-ingress counters and shutdown schema.
Random number generation¶
The Manager environment owns a unilab.managers.ManagerRng as its sole
training-runtime RNG. This adapter wraps one explicit CPU or CUDA
torch.Generator, validates seeds and unsupported sampling semantics, and
returns tensors on that generator’s device. It is not a NumPy
bitstream-compatibility layer. The training-entry seed contract seeds global
Python and Torch RNGs only; it no longer mutates the legacy global NumPy RNG.
The scoped G1 Motion Tracking / FlashSAC owner uses a CUDA ManagerRng for
observation corruption, adaptive/mixed motion sampling, and selected-row reset.
A/B evaluation therefore compares reproducibility and distributions under the
same integer seed, not bitwise NumPy trajectories. Use an explicit env.seed
for deterministic runs; algo.seed seeds the training run’s global Python and
Torch RNGs but does not replace that owner-local generator seed.
ManagerRng.get_state() returns the generator’s CPU state tensor and
set_state() restores it. The exact generator state remains the authoritative
reproducibility payload; an owner that restores or audits RNG records that
state tensor. The runtime keeps no per-transition version accounting.
Runtime evidence¶
The runtime manifest records the effective evidence needed to audit a run:
runtime_limitscontains configured, default, effective, and maximum values for the resolved tensor-runtime knobs.inference_memory_budgetrecords the bounded CUDA inference-ring budget.tensor_memory_budgetrecords the combined CUDA inference, replay storage, replay ingress, learner batch, and workspace budget calculation.cuda_process_sharingrecords validated execution-sharing evidence when the owner explicitly requestsmps; it is absent for the defaultnullmode.
The manifest is written before spawn when a budget decision is made. If an unsafe combination is rejected, the error identifies the offending setting and the process is not launched.
For the single-GPU G1 FlashSAC/MJWarp production workflow, benchmark reproduction, artifacts, and troubleshooting, see Tensor Runtime Production Guide.