Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / auto_parallel / sapp_nd / nd / common / framework_parsers / cost_model_parser_mindspeed.py: 94%

167 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-25 05:45 +0800

1# Copyright 2025-2026 Huawei Technologies Co., Ltd 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================ 

15"""parser child class""" 

16from hyper_parallel.auto_parallel.sapp_nd.nd.common.config import YamlObject 

17from hyper_parallel.auto_parallel.sapp_nd.nd.common.framework_parsers._cost_model_parser import _CostModelParser 

18from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.size import Memory 

19from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.logger import logger 

20from hyper_parallel.auto_parallel.sapp_nd.nd.common.derive import derive 

21 

22 

23class CostModelParserMindspeed(_CostModelParser): 

24 """parser class for MindSpeed format""" 

25 

26 def parse(self): 

27 self.__config_parse_json_multimodals() 

28 

29 # MindSpeed (multimodal) 

30 def __config_parse_json_multimodals(self): 

31 """MindSpeed format for multimodal""" 

32 self.ccfg.device_capacity = Memory.from_gb(55) # 55 * 1024 * 1024 * 1024 

33 self.ccfg.model_name = self.config.model_id 

34 self.config_op_counts(self.ccfg) 

35 # Assume it exists a hook module with the same name as model_name 

36 self.ccfg.n_lay = 0 # SUM ALL 

37 self.ccfg.pp_sched = "1f1b" 

38 self.ccfg.p = self.config.tmp.pp # PP 

39 self.ccfg.m = self.ccfg.p 

40 self.ccfg.b = self.config.tmp.mbs 

41 self.ccfg.d = self.config.tmp.dp # DP 

42 self.ccfg.t = self.config.tmp.tp # TP 

43 self.ccfg.os_max_shard = self.ccfg.d 

44 self.ccfg.cp = self.config.tmp.cp # CP 

45 self.ccfg.vp = self.config.tmp.vpp # VPP 

46 self.ccfg.ep = self.config.tmp.ep # EP 

47 ccfgs = self.__search_and_parse_mods_ccfg(self.ccfg.config) 

48 self.ccfg.multimodal = len(ccfgs) > 1 

49 if len(ccfgs) == 1: 

50 self.__adopt_module(next(iter(ccfgs.values()))) 

51 if self.ccfg.multimodal: 

52 if not self.ccfg.hooks_dict: 

53 raise TypeError( 

54 "Currently for multimodals, 'hook_cls' required for evaluator" 

55 ) 

56 missing_hooks = set(ccfgs.keys()) - set(self.ccfg.hooks_dict.keys()) 

57 if missing_hooks: 

58 raise TypeError( 

59 f"Missing hooks for submodules {list(missing_hooks)}" 

60 ) 

61 self.ccfg.mm_ccfgs = ccfgs 

62 self.ccfg.mm_order = list(ccfgs.keys()) 

63 

64 # Update each mod's offset and pp_partition 

65 for m in self.ccfg.mm_ccfgs: 

66 cc = self.ccfg.mm_ccfgs[m] 

67 num_layer_per_stage = max(1, cc.n_lay // self.ccfg.p // self.ccfg.vp) 

68 if cc.pp_partition: 

69 # print("here",m,cc.pp_partition,num_layer_per_stage) 

70 # Making sure the format is offset[vp][p] like in MF 

71 if isinstance(cc.pp_partition[0], list): 

72 cc.offset = [ 

73 [ 

74 cc.pp_partition[v_idx][idx] - num_layer_per_stage 

75 for idx in range(self.ccfg.p) 

76 ] 

77 for v_idx in range(self.ccfg.vp) 

78 ] 

79 else: 

80 cc.offset = [ 

81 [ 

82 cc.pp_partition[idx] // self.ccfg.vp 

83 - num_layer_per_stage 

84 for idx in range(self.ccfg.p) 

85 ] 

86 for v_idx in range(self.ccfg.vp) 

87 ] 

88 else: 

89 self.__complete_unimodal_pp_plan(m, cc, num_layer_per_stage) 

90 self.ccfg.overwrite_eval_functions = {} 

91 

92 # What a config keeps of its own when it takes its one module's parse. 

93 _KEPT_FROM_CONFIG = ("config", "config_path", "parser", "hooks_dict", "device_capacity", 

94 "multimodal", "mm_ccfgs", "mm_order") 

95 

96 def __adopt_module(self, module): 

97 """Make a config of one module that module: its model, run and derived fields.""" 

98 for key, value in vars(module).items(): 

99 if key not in self._KEPT_FROM_CONFIG: 

100 object.__setattr__(self.ccfg, key, value) 

101 

102 @staticmethod 

103 def __recompute_count(full_rec): 

104 """The layers a module recomputes on each of its stages, as its parse states them.""" 

105 while isinstance(full_rec, list): 

106 full_rec = next((count for count in full_rec if count), 0) 

107 return int(full_rec or 0) 

108 

109 def __completed_recompute(self, cc): 

110 """The module's own recompute, on the stages its completed plan puts it on.""" 

111 own = self.__recompute_count(cc.full_rec) 

112 per_stage = [[min(own, count) for count in chunk] for chunk in cc.pp_partition] 

113 return per_stage if cc.vp > 1 else per_stage[0] 

114 

115 def __complete_unimodal_pp_plan(self, m, cc, num_layer_per_stage): 

116 """Try to follow previous pp plan""" 

117 stage_insert_idx, chunk_insert_idx = 0, 0 

118 cc.offset = [ 

119 [-num_layer_per_stage for _ in range(self.ccfg.p)] 

120 for _ in range(self.ccfg.vp) 

121 ] 

122 previous_mod_idx = ( 

123 self.ccfg.mm_order.index(m) - 1 

124 ) # look for previous mod pp partition 

125 if previous_mod_idx >= 0: 

126 previous_mod_partition = self.ccfg.mm_ccfgs[ 

127 self.ccfg.mm_order[previous_mod_idx] 

128 ].pp_partition 

129 if isinstance(previous_mod_partition[0], list): # vpp 

130 put = False 

131 for v_idx in range(self.ccfg.vp - 1, -1, -1): 

132 for s_idx in range(self.ccfg.p - 1, -1, -1): 

133 if previous_mod_partition[v_idx][s_idx]: 

134 stage_insert_idx = s_idx 

135 chunk_insert_idx = v_idx 

136 put = True 

137 break 

138 if put: 

139 break 

140 else: 

141 stage_insert_idx = self.ccfg.p - 1 

142 for p in previous_mod_partition[::-1]: 

143 if p > 0: 

144 break 

145 stage_insert_idx = max(0, stage_insert_idx - 1) 

146 cc.offset[chunk_insert_idx][stage_insert_idx] = ( 

147 cc.n_lay - num_layer_per_stage 

148 ) 

149 cc.pp_partition = [ 

150 [ 

151 num_layer_per_stage + cc.offset[v_idx][idx] 

152 for idx in range(self.ccfg.p) 

153 ] 

154 for v_idx in range(self.ccfg.vp) 

155 ] 

156 cc.p, cc.vp = self.ccfg.p, self.ccfg.vp 

157 cc.full_rec = self.__completed_recompute(cc) 

158 cc.sel_rec = False 

159 

160 def __search_and_parse_mods_ccfg(self, field): 

161 """extract multimodal submodules (MindSpeed format)""" 

162 res = {} 

163 for _, v in vars(field).items(): 

164 if isinstance(v, YamlObject): 

165 res.update(self.__search_and_parse_mods_ccfg(v)) 

166 if v.model_id: 

167 logger.info("Detected model config: %s", v.model_id) 

168 res[v.model_id] = self.__config_parse_json( 

169 v 

170 ) # Build cost model variable foreach configs 

171 return res 

172 

173 def __config_parse_json_parallelism(self, cc, mod): 

174 """MindSpeed format for parallelism""" 

175 cc.t = max(mod.tensor_model_parallel_size, self.config.tmp.tp) 

176 cc.p = max(mod.pipeline_model_parallel_size, self.config.tmp.pp) 

177 cc.cp = self.config.tmp.cp 

178 cc.d = self.config.tmp.dp 

179 cc.ep = max(mod.expert_model_parallel_size, self.config.tmp.ep) 

180 cc.sequence_parallel = bool(mod.sequence_parallel) 

181 if cc.cp > 1 and cc.sequence_parallel and cc.t > 1: 

182 logger.warning( 

183 "sequence parallelism and context parallelism are both enabled" 

184 ) 

185 cc.pp_partition = ( 

186 mod.pipeline_num_layers 

187 ) # Offset regardless of even distribution 

188 

189 # Interleaving 

190 cc.n_s_split = 1 # seqpipe 

191 cc.pp_sched = "1f1b" 

192 cc.vp = self.config.tmp.vpp # VPP 

193 

194 def __config_parse_json_hyperparameters(self, cc, mod): 

195 """MindSpeed format for hyperparams""" 

196 cc.n_lay = mod.num_layers 

197 cc.h = mod.hidden_size 

198 cc.hff = mod.ffn_hidden_size 

199 cc.v = mod.vocab_size 

200 cc.s = self.config.tmp.seqlen # SEQ_LEN 

201 cc.a = mod.num_attention_heads 

202 cc.n_kv = ( 

203 mod.num_query_groups if mod.num_query_groups else cc.a 

204 ) # NOT SURE 

205 cc.dh = ( 

206 mod.kv_channels if mod.kv_channels else (cc.h / cc.a) 

207 ) # Per head dimension 

208 # print(cc.a, cc.h, cc.dh) 

209 cc.dc_kv = mod.k_lora_rank # KV compression dimension #NOT SURE 

210 cc.dc_q = mod.q_lora_rank # Q compression dimension 

211 cc.dhr = mod.qk_rope_head_dim # decoupled QK per head dimension 

212 cc.qk_norm = bool(mod.qk_layernorm) # Megatron's --qk-layernorm 

213 

214 def __config_parse_json_moe(self, cc, mod): 

215 """MindSpeed format for MoE infos""" 

216 cc.n_exp = max(1, mod.num_moe_experts) 

217 cc.n_chosen_exp = max(1, mod.moe_router_topk) 

218 cc.n_shared_exp = mod.n_shared_exp 

219 cc.cap_fact = 1 

220 cc.t_exp, cc.d_exp = cc.t, cc.d 

221 cc.hff_exp = ( 

222 mod.moe_intermediate_size if mod.moe_intermediate_size else cc.hff 

223 ) 

224 cc.k_1st_dense = mod.first_k_dense_replace 

225 # temporary 

226 cc.etp = self.config.tmp.etp # ETP 

227 

228 def __config_parse_json(self, mod): 

229 """MindSpeed format for unimodal""" 

230 # def mod_hook(M) : 

231 cc = type(self.ccfg)({}) # CostModelConfig({}) 

232 cc.parser = self 

233 cc.config_format = "json" 

234 cc.model_name = mod.model_id 

235 self.config_op_counts(cc) 

236 cc.freeze = mod.freeze # for later 

237 cc.has_fa = True 

238 cc.has_op = True # mod.use_distributed_optimizer 

239 cc.has_grad_shard = True 

240 # cc.vp_less_mem = False 

241 cc.has_clip = False 

242 cc.cp_algo = "colossalai_cp" 

243 cc.gmm = mod.moe_grouped_gemm 

244 cc.vocab_emb_dp = False 

245 cc.emb_dp_sharded = True 

246 

247 cc.offset = 0 

248 # Parallel dimensions 

249 self.__config_parse_json_parallelism(cc, mod) 

250 

251 cc.full_rec = mod.recompute_num_layers 

252 cc.sel_rec = False # selective recompute is not parsed 

253 cc.sel_rec_rule = "hyperparallel" 

254 if mod.recompute_num_layers and isinstance( 

255 mod.recompute_num_layers, int 

256 ): 

257 cc.full_rec = [mod.recompute_num_layers] * cc.p 

258 if cc.vp > 1: 

259 cc.full_rec = [cc.full_rec] * cc.vp 

260 

261 # Hyperparameters 

262 self.__config_parse_json_hyperparameters(cc, mod) 

263 

264 # Microbatch infos 

265 cc.b = self.config.tmp.mbs # MBS # Microbatch size 

266 cc.m = cc.p # Number of microbatches 

267 # if cc.m<=0 : logger.warning("num_micro is negative") 

268 

269 # MoE infos 

270 self.__config_parse_json_moe(cc, mod) 

271 

272 # FP byte storages 

273 cc.bytes_p = self.ccfg.fp_bytes(mod.params_dtype) # parameters 

274 cc.bytes_compute = 2 

275 cc.bytes_softmax = ( 

276 4 if mod.attention_softmax_in_fp32 else 2 

277 ) # softmax output 

278 

279 # Optimizer parallel factors 

280 cc.os_max_shard = cc.d 

281 

282 cc.gbs = cc.b * cc.d * cc.m 

283 cc.n_mtp = mod.mtp_num_layers 

284 self.config_layer_stack(cc) 

285 cc.overwrite_eval_functions = {} 

286 derive(cc) 

287 return cc # mod_hook