Skip to content

Commit 6292490

Browse files
ChromeHeartsOrbax Authors
authored andcommitted
Internal
PiperOrigin-RevId: 947201204
1 parent cff966f commit 6292490

19 files changed

Lines changed: 1791 additions & 261 deletions

checkpoint/orbax/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import contextlib
2020
import functools
2121

22+
2223
from orbax.checkpoint import arrays
2324
from orbax.checkpoint import aggregate_handlers
2425
from orbax.checkpoint import args

checkpoint/orbax/checkpoint/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import contextlib
2020
import functools
2121

22+
2223
from orbax.checkpoint import arrays
2324
from orbax.checkpoint import aggregate_handlers
2425
from orbax.checkpoint import args

checkpoint/orbax/checkpoint/checkpoint_manager.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import asyncio
1920
import concurrent
2021
import dataclasses
2122
import datetime
@@ -61,10 +62,10 @@
6162
from orbax.checkpoint._src.path import step as step_lib
6263
from orbax.checkpoint._src.path import temporary_paths
6364
from orbax.checkpoint._src.path import utils as path_utils
65+
from orbax.checkpoint.experimental.tiering_service import client as cts_client
6466
from typing_extensions import Self # for Python version < 3.11
6567

6668

67-
6869
PyTree = Any
6970
CheckpointDirs = Tuple[str, str]
7071
SaveParams = Mapping[str, Any]
@@ -96,6 +97,7 @@
9697
MultiprocessingOptions = options_lib.MultiprocessingOptions
9798
FileOptions = options_lib.FileOptions
9899

100+
99101
DEFAULT_ITEM_NAME = 'default'
100102
METRIC_ITEM_NAME = 'metrics'
101103
METADATA_ITEM_NAME = 'metadata'
@@ -407,10 +409,11 @@ class CheckpointManagerOptions:
407409
None
408410
)
409411
prevent_write_metrics: bool = False
410-
# TODO(b/428061876) Remove this option.
411412
enable_should_save_is_saving_in_progress_check: bool = True
413+
# TODO(b/428061876) Remove this option.
412414
enable_per_process_directory_creation: bool = False
413415
lightweight_initialize: bool = False
416+
tiering_client: Optional[cts_client.TieringClient] = None
414417

415418
def __post_init__(self):
416419
step_name_format_single_host_load_and_broadcast = (
@@ -719,6 +722,8 @@ def __init__(
719722

720723
self._options = options or CheckpointManagerOptions()
721724
self._multiprocessing_options = self._options.multiprocessing_options
725+
self._tiering_client = self._options.tiering_client
726+
self._tiering_uuids = {} # maps step (int) -> uuid (str)
722727

723728
if self._options.enable_per_process_directory_creation:
724729
future.AwaitableSignalsContract.awaitable_signals_contract_prefix += (
@@ -957,6 +962,7 @@ def _configure_checkpointer_common(
957962
options: CheckpointManagerOptions,
958963
use_async: bool,
959964
) -> Checkpointer:
965+
kwargs = {}
960966
if use_async:
961967
return async_checkpointer.AsyncCheckpointer(
962968
handler,
@@ -965,6 +971,7 @@ def _configure_checkpointer_common(
965971
file_options=options.file_options,
966972
checkpoint_metadata_store=self._non_blocking_metadata_store,
967973
temporary_path_class=options.temporary_path_class,
974+
**kwargs,
968975
)
969976
else:
970977
return Checkpointer(
@@ -973,6 +980,7 @@ def _configure_checkpointer_common(
973980
file_options=options.file_options,
974981
checkpoint_metadata_store=self._blocking_metadata_store,
975982
temporary_path_class=options.temporary_path_class,
983+
**kwargs,
976984
)
977985

978986
def _configure_checkpointer_legacy_init(
@@ -1516,6 +1524,17 @@ def save(
15161524
args = args_lib.Composite(**args_dict)
15171525

15181526
save_directory = self._get_write_step_directory(step, self.directory)
1527+
if self._tiering_client is not None:
1528+
logging.info('[CTS] Reserving path: %s', save_directory)
1529+
uuid, lustre_path = self._run_async(
1530+
self._tiering_client.reserve(str(save_directory))
1531+
)
1532+
logging.info(
1533+
'[CTS] Reserved path. UUID: %s, Lustre Path: %s', uuid, lustre_path
1534+
)
1535+
self._tiering_uuids[step] = uuid
1536+
save_directory = epath.Path(lustre_path)
1537+
15191538
logging.info(
15201539
'[process=%s] Saving checkpoint at step %d', process_index, step
15211540
)
@@ -1737,8 +1756,24 @@ def restore(
17371756
args = typing.cast(args_lib.Composite, args)
17381757

17391758
restore_directory = self._get_read_step_directory(step, directory)
1759+
if self._tiering_client is not None:
1760+
logging.info('[CTS] Prefetching path: %s', restore_directory)
1761+
1762+
async def _do_prefetch():
1763+
fut = await self._tiering_client.prefetch(str(restore_directory))
1764+
return await fut
1765+
1766+
lustre_path = self._run_async(_do_prefetch())
1767+
logging.info('[CTS] Prefetch complete. Lustre Path: %s', lustre_path)
1768+
restore_directory = epath.Path(lustre_path)
1769+
17401770
step_stats.checkpointer_start_time = time.time()
17411771
restored = self._checkpointer.restore(restore_directory, args=args)
1772+
if self._tiering_client is not None:
1773+
logical_restore_dir = self._get_read_step_directory(step, directory)
1774+
self._run_async(
1775+
self._tiering_client.release_path(str(logical_restore_dir))
1776+
)
17421777
step_stats.checkpointer_duration_secs = (
17431778
time.time() - step_stats.checkpointer_start_time
17441779
)
@@ -2153,6 +2188,12 @@ def _finalize(self, step: int, steps_to_remove: List[int]):
21532188
# If an error is encountered while waiting for commit futures to complete,
21542189
# we will not proceed past this point.
21552190
self._finalize_checkpoint(step)
2191+
if self._tiering_client is not None and step in self._tiering_uuids:
2192+
uuid = self._tiering_uuids[step]
2193+
logging.info('[CTS] Finalizing step %d, UUID: %s', step, uuid)
2194+
self._run_async(self._tiering_client.finalize(uuid))
2195+
del self._tiering_uuids[step]
2196+
21562197
remove_steps_start_time = time.time()
21572198
self._checkpoint_deleter.delete_steps(steps_to_remove)
21582199
jax.monitoring.record_event_duration_secs(
@@ -2179,6 +2220,20 @@ def _finalize(self, step: int, steps_to_remove: List[int]):
21792220
# This time is tracked for metric purposes only.
21802221
self._last_save_time = time.time()
21812222

2223+
def _run_async(self, coro):
2224+
2225+
try:
2226+
loop = asyncio.get_event_loop()
2227+
except RuntimeError:
2228+
loop = asyncio.new_event_loop()
2229+
asyncio.set_event_loop(loop)
2230+
if loop.is_running():
2231+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
2232+
fut = executor.submit(asyncio.run, coro)
2233+
return fut.result()
2234+
else:
2235+
return loop.run_until_complete(coro)
2236+
21822237
def close(self):
21832238
"""Waits for outstanding operations to finish and closes internal objects."""
21842239
self.wait_until_finished()

0 commit comments

Comments
 (0)