Skip to content

Commit 6aeadb6

Browse files
committed
Clean up
Signed-off-by: Hollow Man <hollowman@opensuse.org>
1 parent 4d19cdf commit 6aeadb6

17 files changed

Lines changed: 458 additions & 225 deletions

File tree

docs/design-docs/checkpoint-engines.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ policy:
7777
```
7878

7979
The factory passes `bucket_size` in bytes plus the selected backend kwargs to
80-
the backend constructor. It also provides a backend-neutral default `device`
81-
unless the config already specifies one.
80+
the backend constructor. Backend-specific settings such as transfer device,
81+
cleanup behavior, and transport plugin name live in config.
8282

8383
## Backend Interface
8484

@@ -100,7 +100,7 @@ from nemo_rl.utils.checkpoint_engines import (
100100
class MyCheckpointEngine(CheckpointEngine):
101101
cleanup_after_load = True
102102
103-
def __init__(self, bucket_size: int, device: str | torch.device = "cuda"):
103+
def __init__(self, bucket_size: int, device: str | torch.device):
104104
self.bucket_size = bucket_size
105105
self.device = torch.device(device)
106106
@@ -245,6 +245,8 @@ policy:
245245
backend: nixl
246246
engine_kwargs:
247247
nixl:
248+
device: cuda
249+
cleanup_after_load: false
248250
backend_name: UCX
249251
backend_init_params:
250252
ucx_error_handling_mode: peer

docs/guides/checkpoint-engine-refit.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ policy:
7777
backend: nixl
7878
engine_kwargs:
7979
nixl:
80+
device: cpu
81+
cleanup_after_load: false
8082
backend_name: UCX
8183
backend_init_params:
8284
ucx_error_handling_mode: peer

examples/configs/grpo_math_1B.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ policy:
330330
update_weights_bucket_megabytes: 2048
331331
engine_kwargs:
332332
# For plugin backends, key kwargs by the exact backend string.
333-
nixl: {}
333+
nixl:
334+
device: cuda
335+
cleanup_after_load: true
336+
backend_name: UCX
334337
colocated:
335338
# true: generation shares training GPUs
336339
# false: uses dedicated generation resources

examples/configs/grpo_math_8B.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ policy:
6565
update_weights_bucket_megabytes: 2048
6666
engine_kwargs:
6767
# For plugin backends, key kwargs by the exact backend string.
68-
nixl: {}
68+
nixl:
69+
device: cuda
70+
cleanup_after_load: true
71+
backend_name: UCX
6972

7073
cluster:
7174
gpus_per_node: 8

nemo_rl/algorithms/grpo.py

Lines changed: 31 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
run_async_nemo_gym_rollout,
7373
run_multi_turn_rollout,
7474
)
75+
from nemo_rl.models.generation.constants import SGLANG_BACKEND, VLLM_BACKEND
7576
from nemo_rl.models.generation.interfaces import GenerationInterface
7677
from nemo_rl.models.generation.sglang import SGLangConfig, SGLangGeneration
7778
from nemo_rl.models.generation.vllm import VllmConfig, VllmGeneration
@@ -88,6 +89,7 @@
8889
from nemo_rl.utils.nsys import maybe_gpu_profile_step
8990
from nemo_rl.utils.timer import TimeoutChecker, Timer
9091
from nemo_rl.utils.venvs import create_local_venv_on_each_node
92+
from nemo_rl.weight_sync import create_weight_synchronizer
9193

9294
# ===============================================================================
9395
# Configuration
@@ -1188,16 +1190,6 @@ def _create_advantage_estimator(master_config: MasterConfig):
11881190
return adv_estimator
11891191

11901192

1191-
def _flatten_checkpoint_engine_metadata(metadata_results: list[Any]) -> list[Any]:
1192-
metadata = []
1193-
for worker_metadata in metadata_results:
1194-
if isinstance(worker_metadata, list):
1195-
metadata.extend(worker_metadata)
1196-
else:
1197-
metadata.append(worker_metadata)
1198-
return metadata
1199-
1200-
12011193
def _get_enabled_checkpoint_engine_config(
12021194
checkpoint_engine_config: dict[str, Any] | None,
12031195
) -> dict[str, Any] | None:
@@ -1206,71 +1198,6 @@ def _get_enabled_checkpoint_engine_config(
12061198
return checkpoint_engine_config
12071199

12081200

1209-
def _refit_policy_generation_with_checkpoint_engine(
1210-
policy: ColocatablePolicyInterface,
1211-
policy_generation: GenerationInterface,
1212-
checkpoint_engine_config: dict[str, Any],
1213-
kv_scales: Optional[dict[str, float]] = None,
1214-
) -> bool:
1215-
backend = checkpoint_engine_config["backend"]
1216-
bucket_size_bytes = (
1217-
checkpoint_engine_config["update_weights_bucket_megabytes"] * 1024 * 1024
1218-
)
1219-
engine_kwargs = checkpoint_engine_config["engine_kwargs"][backend]
1220-
1221-
try:
1222-
init_futures = policy.init_checkpoint_engine(
1223-
backend=backend,
1224-
bucket_size_bytes=bucket_size_bytes,
1225-
engine_kwargs=engine_kwargs,
1226-
) + policy_generation.init_checkpoint_engine(
1227-
backend=backend,
1228-
bucket_size_bytes=bucket_size_bytes,
1229-
engine_kwargs=engine_kwargs,
1230-
)
1231-
ray.get(init_futures)
1232-
1233-
policy_metadata = _flatten_checkpoint_engine_metadata(
1234-
ray.get(policy.prepare_checkpoint_engine())
1235-
)
1236-
generation_metadata = _flatten_checkpoint_engine_metadata(
1237-
ray.get(policy_generation.prepare_checkpoint_engine())
1238-
)
1239-
1240-
train_world_size = len(policy_metadata)
1241-
rollout_world_size = len(generation_metadata)
1242-
metadata = policy_metadata + generation_metadata
1243-
ray.get(
1244-
policy.init_checkpoint_engine_process_group(
1245-
metadata=metadata,
1246-
train_world_size=train_world_size,
1247-
rollout_world_size=rollout_world_size,
1248-
)
1249-
+ policy_generation.init_checkpoint_engine_process_group(
1250-
metadata=metadata,
1251-
train_world_size=train_world_size,
1252-
rollout_world_size=rollout_world_size,
1253-
)
1254-
)
1255-
1256-
futures_train = policy.send_weights_via_checkpoint_engine(kv_scales=kv_scales)
1257-
futures_inference = policy_generation.update_weights_from_checkpoint_engine()
1258-
ray.get(futures_train)
1259-
results = ray.get(futures_inference)
1260-
return all(result for result in results if result is not None)
1261-
finally:
1262-
try:
1263-
ray.get(
1264-
policy.finalize_checkpoint_engine()
1265-
+ policy_generation.finalize_checkpoint_engine()
1266-
)
1267-
except Exception as finalize_error:
1268-
warnings.warn(
1269-
f"Failed to finalize checkpoint-engine refit state: {finalize_error}",
1270-
RuntimeWarning,
1271-
)
1272-
1273-
12741201
def refit_policy_generation(
12751202
policy: ColocatablePolicyInterface,
12761203
policy_generation: GenerationInterface,
@@ -1300,101 +1227,44 @@ def refit_policy_generation(
13001227
"policy.generation.checkpoint_engine.enabled."
13011228
)
13021229

1303-
if colocated_inference:
1304-
policy.offload_before_refit()
1305-
policy_generation.prepare_for_generation(tags=["weights"])
1230+
if colocated_inference or checkpoint_engine_config is not None:
1231+
generation_backend = (
1232+
SGLANG_BACKEND
1233+
if isinstance(policy_generation, SGLangGeneration)
1234+
else VLLM_BACKEND
1235+
)
1236+
weight_sync = create_weight_synchronizer(
1237+
policy=policy,
1238+
generation=policy_generation,
1239+
generation_backend=generation_backend,
1240+
colocated=colocated_inference,
1241+
refit_buffer_size_gb=_refit_buffer_size_gb,
1242+
)
1243+
weight_sync.sync_weights(timer=timer, kv_scales=kv_scales)
1244+
return
13061245

1307-
# Create a context manager that does nothing when timer is None
13081246
timer_context = (
13091247
timer.time("prepare_for_generation/transfer_and_update_weights")
13101248
if timer is not None
13111249
else nullcontext()
13121250
)
13131251
with timer_context:
1314-
# update weights
1315-
update_success = False
1316-
if colocated_inference:
1317-
# get model param keys, which is grouped by size
1318-
if _refit_buffer_size_gb is not None:
1319-
buffer_size_bytes = _refit_buffer_size_gb * (1024**3)
1320-
else:
1321-
# Empirically sets ratio as 30% to maximize efficiency.
1322-
# The remaining 70% is a necessary buffer reserved for the parameter all-gathering across the expert-parallelism dimension.
1323-
memory_ratio = os.getenv("NRL_REFIT_BUFFER_MEMORY_RATIO", "0.3")
1324-
buffer_size_bytes = int(
1325-
policy.get_free_memory_bytes() * float(memory_ratio)
1326-
)
1327-
1328-
if isinstance(policy_generation, SGLangGeneration):
1329-
sglang_url_to_gpu_uuids = (
1330-
policy_generation.get_sglang_url_to_gpu_uuids()
1331-
)
1332-
# Stream weights via HTTP
1333-
flush_success = policy_generation.invalidate_kv_cache()
1334-
if not flush_success:
1335-
print("SGLang KV cache invalidation failed before weight update. ")
1336-
futures_train = policy.stream_weights_via_http(
1337-
sglang_url_to_gpu_uuids=sglang_url_to_gpu_uuids,
1338-
)
1339-
# Wait for all workers to complete
1340-
ray.get(futures_train)
1341-
update_success = True
1342-
else:
1343-
# Original ZMQ IPC path for vLLM
1344-
futures_train = policy.stream_weights_via_ipc_zmq(
1345-
buffer_size_bytes=buffer_size_bytes
1346-
)
1347-
futures_inference = policy_generation.update_weights_via_ipc_zmq()
1348-
# wait for all futures to complete
1349-
ray.get(futures_train)
1350-
results = ray.get(futures_inference)
1351-
update_success = all(result for result in results if result is not None)
1352-
else:
1353-
if checkpoint_engine_config is not None:
1354-
if isinstance(policy_generation, SGLangGeneration):
1355-
raise NotImplementedError(
1356-
"SGLang does not support checkpoint-engine non-colocated refit."
1357-
)
1358-
update_success = _refit_policy_generation_with_checkpoint_engine(
1359-
policy,
1360-
policy_generation,
1361-
checkpoint_engine_config,
1362-
kv_scales=kv_scales,
1363-
)
1364-
else:
1365-
# update weights through nccl
1366-
# SGLang haven't implemented non-colocated inference mode.
1367-
if isinstance(policy_generation, SGLangGeneration):
1368-
raise NotImplementedError(
1369-
"SGLang haven't implemented non-colocated inference mode. "
1370-
)
1371-
futures_train = policy.broadcast_weights_for_collective(
1372-
kv_scales=kv_scales
1373-
)
1374-
futures_inference = policy_generation.update_weights_from_collective()
1375-
# wait for all futures to complete
1376-
ray.get(futures_train)
1377-
results = ray.get(futures_inference)
1378-
update_success = all(result for result in results if result is not None)
1379-
1380-
# check if update is successful
1381-
if not update_success:
1382-
if colocated_inference:
1383-
error_tag = "cuda-ipc"
1384-
elif checkpoint_engine_config is not None:
1385-
error_tag = checkpoint_engine_config["backend"]
1386-
else:
1387-
error_tag = "nccl"
1388-
error_message = (
1389-
"❌ Error: Updating weights for the generation policy failed during refit.\n"
1390-
f"This often indicates an issue with {error_tag} or "
1391-
"a problem within the generation backend (e.g., vLLM worker).\n"
1252+
if isinstance(policy_generation, SGLangGeneration):
1253+
raise NotImplementedError(
1254+
"SGLang haven't implemented non-colocated inference mode. "
13921255
)
1393-
raise RuntimeError(error_message)
1256+
futures_train = policy.broadcast_weights_for_collective(kv_scales=kv_scales)
1257+
futures_inference = policy_generation.update_weights_from_collective()
1258+
ray.get(futures_train)
1259+
results = ray.get(futures_inference)
1260+
update_success = all(result for result in results if result is not None)
13941261

1395-
if colocated_inference:
1396-
policy.offload_after_refit()
1397-
policy_generation.prepare_for_generation(tags=["kv_cache"])
1262+
if not update_success:
1263+
raise RuntimeError(
1264+
"❌ Error: Updating weights for the generation policy failed during refit.\n"
1265+
"This often indicates an issue with nccl or a problem within the "
1266+
"generation backend (e.g., vLLM worker).\n"
1267+
)
13981268

13991269

14001270
def _log_mixed_rewards_and_advantages_information(

nemo_rl/models/generation/vllm/vllm_backend.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def maybe_preinit_nixl_for_vllm_worker(
4949
if wrapper_state.get("_nrl_nixl_preinit_agent") is not None:
5050
return
5151

52-
backend_name = preinit_config.get("backend_name", "UCX")
52+
backend_name = preinit_config["backend_name"]
5353
backend_init_params = preinit_config.get("backend_init_params")
5454

5555
from nemo_rl.utils.checkpoint_engines.nixl import preinit_nixl_agent
@@ -122,7 +122,6 @@ def init_checkpoint_engine(
122122
backend,
123123
bucket_size_bytes=bucket_size_bytes,
124124
engine_kwargs=engine_kwargs,
125-
default_device=self.device,
126125
)
127126

128127
def prepare_checkpoint_engine(self) -> Any:

nemo_rl/models/generation/vllm/vllm_worker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ def _patch_vllm_worker_nixl_preinit():
284284

285285
nixl_kwargs = checkpoint_cfg["engine_kwargs"]["nixl"]
286286
preinit_config = {
287-
"backend_name": nixl_kwargs.get("backend_name", "UCX"),
287+
"backend_name": nixl_kwargs["backend_name"],
288288
"backend_init_params": nixl_kwargs.get("backend_init_params"),
289289
}
290290
old_snippet = (

nemo_rl/models/policy/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def maybe_preinit_nixl_for_checkpoint_engine(config: Any) -> Any | None:
239239
from nemo_rl.utils.checkpoint_engines.nixl import preinit_nixl_agent
240240

241241
return preinit_nixl_agent(
242-
backend_name=nixl_kwargs.get("backend_name", "UCX"),
242+
backend_name=nixl_kwargs["backend_name"],
243243
backend_init_params=nixl_kwargs.get("backend_init_params"),
244244
)
245245

nemo_rl/models/policy/workers/base_policy_worker.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,10 @@ def init_checkpoint_engine(
5858

5959
from nemo_rl.utils.checkpoint_engine import create_checkpoint_engine
6060

61-
default_device = (
62-
torch.device("cuda", torch.cuda.current_device())
63-
if torch.cuda.is_available()
64-
else torch.device("cpu")
65-
)
6661
self.checkpoint_engine = create_checkpoint_engine(
6762
backend,
6863
bucket_size_bytes=bucket_size_bytes,
6964
engine_kwargs=engine_kwargs,
70-
default_device=default_device,
7165
)
7266

7367
def prepare_checkpoint_engine(self) -> Any:

nemo_rl/utils/checkpoint_engines/base.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,12 @@ def create_checkpoint_engine(
182182
*,
183183
bucket_size_bytes: int,
184184
engine_kwargs: dict[str, Any],
185-
default_device: Any,
186185
) -> CheckpointEngine:
187-
"""Create a checkpoint engine with a backend-neutral device default."""
188-
kwargs = dict(engine_kwargs)
189-
kwargs.setdefault("device", default_device)
186+
"""Create a checkpoint engine from backend-specific configuration."""
190187
return CheckpointEngineRegistry.new(
191188
backend,
192189
bucket_size=bucket_size_bytes,
193-
**kwargs,
190+
**engine_kwargs,
194191
)
195192

196193

0 commit comments

Comments
 (0)