@@ -60,6 +60,35 @@ def sample_random_actions(env) -> torch.Tensor | dict[str, torch.Tensor]:
6060 return 2.0 * torch .rand (u .num_envs , u .single_action_space .shape [0 ], device = u .device ) - 1.0
6161
6262
63+ def _find_cuda_devices (value ) -> set [str ]:
64+ """Collect CUDA device names from nested tensor-like values."""
65+ import torch # noqa: PLC0415
66+
67+ devices : set [str ] = set ()
68+
69+ def collect (item ) -> None :
70+ if isinstance (item , dict ):
71+ for nested in item .values ():
72+ collect (nested )
73+ return
74+ if isinstance (item , (list , tuple )):
75+ for nested in item :
76+ collect (nested )
77+ return
78+ device = item if isinstance (item , (str , torch .device )) else getattr (item , "device" , None )
79+ if device is None :
80+ return
81+ try :
82+ device = torch .device (device )
83+ except (RuntimeError , TypeError ):
84+ return
85+ if device .type == "cuda" :
86+ devices .add (str (device ))
87+
88+ collect (value )
89+ return devices
90+
91+
6392class EnvironmentStepTimingRecorder (AbstractContextManager ):
6493 """Record host-return step time or an optional synchronized step breakdown.
6594
@@ -119,6 +148,7 @@ def __enter__(self) -> EnvironmentStepTimingRecorder:
119148 self ._original_env_step = self ._env .step
120149
121150 if self ._measure_synchronized_step_breakdown :
151+ import torch # noqa: PLC0415
122152 import warp as wp # noqa: PLC0415
123153
124154 from isaaclab .utils .timer import Timer # noqa: PLC0415
@@ -130,34 +160,50 @@ def __enter__(self) -> EnvironmentStepTimingRecorder:
130160 self ._simulation_step_calls = 0
131161 assert self ._simulation_context is not None
132162 self ._original_sim_step = self ._simulation_context .step
163+ environment_cuda_devices = _find_cuda_devices (getattr (self ._env .unwrapped , "device" , None ))
164+ active_cuda_devices = environment_cuda_devices
165+
166+ def synchronize_torch (devices : set [str ]) -> None :
167+ for device in sorted (devices ):
168+ torch .cuda .synchronize (device )
133169
134170 def timed_simulation_step (* args , ** kwargs ):
135171 if not self ._inside_environment_step :
136172 return self ._original_sim_step (* args , ** kwargs )
173+ synchronize_torch (active_cuda_devices )
137174 wp .synchronize ()
138175 timer = Timer ()
176+ timer .start ()
139177 try :
140- with timer :
141- return self ._original_sim_step (* args , ** kwargs )
178+ return self ._original_sim_step (* args , ** kwargs )
142179 finally :
180+ synchronize_torch (active_cuda_devices )
181+ timer .stop ()
143182 self ._simulation_total_time_s += timer .total_run_time
144183 self ._simulation_step_calls += 1
145184
146185 self ._simulation_context .step = timed_simulation_step
147186
148187 def timed_environment_step (* args , ** kwargs ):
188+ nonlocal active_cuda_devices
149189 recording = self ._environment_step_index >= self ._warmup_steps
150190 self ._environment_step_index += 1
151191 simulation_start_time_s = self ._simulation_total_time_s
152192 simulation_start_calls = self ._simulation_step_calls
193+ previous_cuda_devices = active_cuda_devices
194+ active_cuda_devices = environment_cuda_devices | _find_cuda_devices (args ) | _find_cuda_devices (kwargs )
195+ synchronize_torch (active_cuda_devices )
153196 wp .synchronize ()
154197 timer = Timer ()
198+ timer .start ()
155199 self ._inside_environment_step = True
156200 try :
157- with timer :
158- return self ._original_env_step (* args , ** kwargs )
201+ return self ._original_env_step (* args , ** kwargs )
159202 finally :
160203 self ._inside_environment_step = False
204+ synchronize_torch (active_cuda_devices )
205+ timer .stop ()
206+ active_cuda_devices = previous_cuda_devices
161207 if recording :
162208 self .step_times_s .append (timer .total_run_time )
163209 self .simulation_step_times_s .append (self ._simulation_total_time_s - simulation_start_time_s )
0 commit comments