1+ import inspect
2+ import warnings
3+ from pathlib import Path
4+ from typing import Optional
5+
6+ import torch
7+ import torch .distributed as dist
8+ import torch .distributed .checkpoint as dcp
19import torch .distributed .checkpoint .default_planner as torch_default_runner
10+ from torch .distributed .checkpoint import state_dict_saver
11+ from torch .distributed .checkpoint .default_planner import DefaultSavePlanner
12+ from torch .distributed .checkpoint .logger import _dcp_method_logger
13+ from torch .distributed .checkpoint .metadata import STATE_DICT_TYPE , Metadata
14+ from torch .distributed .checkpoint .planner import SavePlan , SavePlanner
15+ from torch .distributed .checkpoint .storage import StorageWriter
16+ from torch .distributed .checkpoint .utils import _DistWrapper
217
318
419def fake_validate_global_plan (* args , ** kwargs ):
@@ -7,3 +22,111 @@ def fake_validate_global_plan(*args, **kwargs):
722
823def patch_default_save_plan ():
924 torch_default_runner ._validate_global_plan = fake_validate_global_plan
25+
26+
27+ def _xtuner_save_state_dict (
28+ state_dict : STATE_DICT_TYPE ,
29+ storage_writer : StorageWriter ,
30+ process_group : Optional [dist .ProcessGroup ] = None ,
31+ coordinator_rank : int = 0 ,
32+ no_dist : bool = False ,
33+ planner : Optional [SavePlanner ] = None ,
34+ ) -> Metadata :
35+ torch ._C ._log_api_usage_once ("torch.distributed.checkpoint.save_state_dict" )
36+
37+ distW = _DistWrapper (process_group , not no_dist , coordinator_rank )
38+ if planner is None :
39+ planner = DefaultSavePlanner ()
40+ assert planner is not None
41+
42+ global_metadata = None
43+
44+ ckpt_kwargs = {}
45+ if (ckpt_id := getattr (storage_writer , "checkpoint_id" , None )) is not None :
46+ ckpt_kwargs ["checkpoint_id" ] = ckpt_id
47+ ckpt_kwargs ["process_group" ] = distW .group
48+
49+ @_dcp_method_logger (** ckpt_kwargs )
50+ def local_step ():
51+ assert planner is not None
52+ storage_meta = storage_writer .storage_meta ()
53+ if "storage_meta" not in inspect .signature (planner .set_up_planner ).parameters :
54+ warnings .warn (
55+ "The function definition for SavePlanner.set_up_planner has been updated"
56+ " to include the storage_meta argument. Please update your implementation"
57+ " to include this parameter."
58+ )
59+ planner .set_up_planner (state_dict , distW .is_coordinator ) # type: ignore[call-arg, arg-type]
60+ else :
61+ planner .set_up_planner (
62+ state_dict = state_dict ,
63+ storage_meta = storage_meta ,
64+ is_coordinator = distW .is_coordinator ,
65+ )
66+ storage_writer .set_up_storage_writer (distW .is_coordinator )
67+
68+ local_plan = planner .create_local_plan ()
69+ local_plan = storage_writer .prepare_local_plan (local_plan )
70+ return local_plan
71+
72+ @_dcp_method_logger (** ckpt_kwargs )
73+ def global_step (all_local_plans ):
74+ nonlocal global_metadata
75+
76+ assert planner is not None
77+ all_local_plans , global_metadata = planner .create_global_plan (all_local_plans )
78+ all_local_plans = storage_writer .prepare_global_plan (all_local_plans )
79+ return all_local_plans
80+
81+ central_plan : SavePlan = distW .reduce_scatter ("plan" , local_step , global_step )
82+
83+ @_dcp_method_logger (** ckpt_kwargs )
84+ def write_data ():
85+ assert planner is not None
86+ final_local_plan = planner .finish_plan (central_plan )
87+ all_writes = storage_writer .write_data (final_local_plan , planner )
88+
89+ all_writes .wait ()
90+ return all_writes .value ()
91+
92+ @_dcp_method_logger (** ckpt_kwargs )
93+ def finish_checkpoint (all_results ):
94+ assert global_metadata is not None
95+ storage_writer .finish (metadata = global_metadata , results = all_results )
96+ # return global_metadata
97+ return Metadata (state_dict_metadata = {}) # This is a patch to avoid broadcast overhead.
98+
99+ return distW .all_reduce ("write" , write_data , finish_checkpoint )
100+
101+
102+ def patch_dcp_save_state_dict ():
103+ if hasattr (state_dict_saver , "_save_state_dict" ):
104+ original = getattr (state_dict_saver , "_save_state_dict" )
105+ if callable (original ):
106+ state_dict_saver ._save_state_dict = _xtuner_save_state_dict
107+
108+
109+ def patch_dcp_save_with_cache_storage ():
110+ original_dcp_save = dcp .save
111+
112+ def dcp_save_with_cache_storage (state_dict , ** kwargs ):
113+ checkpoint_id = kwargs .get ("checkpoint_id" , None )
114+ assert checkpoint_id is not None , "checkpoint_id is required for caching mechanism."
115+ checkpoint_id = checkpoint_id if isinstance (checkpoint_id , Path ) else Path (checkpoint_id )
116+ planner = kwargs .get ("planner" , None )
117+ storage_writer = kwargs .get ("storage_writer" , None )
118+
119+ if storage_writer is None and planner is None :
120+ from xtuner .v1 .patch .xtuner_cache_planner import XtunerCacheSavePlanner
121+ from xtuner .v1 .patch .xtuner_storage import XtunerCacheWriter
122+
123+ planner = XtunerCacheSavePlanner (enable_plan_caching = True , cache_key_prefix = checkpoint_id .stem )
124+ storage_writer = XtunerCacheWriter (
125+ checkpoint_id , enable_write_result_caching = True , cache_key_prefix = checkpoint_id .stem
126+ )
127+ kwargs ["planner" ] = planner
128+ kwargs ["storage_writer" ] = storage_writer
129+
130+ return original_dcp_save (state_dict , ** kwargs )
131+
132+ dcp .save = dcp_save_with_cache_storage
0 commit comments