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

192 statements  

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

1# Copyright 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"""The derived fields of a cost-model config, computed in one place. 

16 

17A parser states primary facts: the model's dimensions, the parallel strategy 

18and the fixed facts of the run. :func:`derive` computes what follows from 

19them, once when a config is parsed and again whenever its strategy changes, 

20where each parser used to compute it. What a producer does not state, its 

21model's family says, from its op profile: :func:`derive_family`. 

22""" 

23import logging 

24import math 

25from typing import Any, Dict, Mapping, Union 

26 

27from hyper_parallel.auto_parallel._exec_spec import RECOMPUTE_OPS 

28from hyper_parallel.auto_parallel._op_profiles import VISION_ARCH, family_profile 

29from hyper_parallel.auto_parallel.sapp_nd.nd.common.config import Config 

30from hyper_parallel.auto_parallel.sapp_nd.nd.common.layer_order import stated_recompute 

31 

32logger = logging.getLogger(__name__) 

33 

34# The recompute switches of HyperParallel's selective activation checkpointing 

35# (hyper_parallel/distributed/activation_checkpoint.py). It keeps the outputs of 

36# matmul and attention kernels and of reduce-scatter, all-to-all and all-reduce, 

37# and recomputes everything else, all-gathers included. A switch at 1 keeps the 

38# op's activation and 0 recomputes it. The policy also recomputes every other 

39# projection matmul, which has no switch, so that part is not priced; an MLA 

40# layer's up-projections, matmuls too, are kept. 

41HYPER_SELECTIVE_REC_OP = { 

42 "attBMM": 1, 

43 "headCast": 0, 

44 "dropout": 0, 

45 "softmax": 0, 

46 "normOp": 0, 

47 "gather": 0, 

48 "ffAct": 0, 

49 "attUp": 1, 

50} 

51 

52 

53def runs_hyper_selective(ccfg: Any, switches: Any = None) -> bool: 

54 """Whether *ccfg*'s selective layers run HyperParallel's policy: their switches, or *switches*, are its switches. 

55 

56 A switch they leave out keeps its op. 

57 """ 

58 stated = getattr(ccfg, "rec_op", None) if switches is None else switches 

59 values = stated if isinstance(stated, Mapping) else vars(stated) if stated is not None else {} 

60 return all(values.get(name, 1) == state for name, state in HYPER_SELECTIVE_REC_OP.items()) 

61 

62 

63def derive_sequence_parallel(ccfg: Any) -> None: 

64 """Set the sequence-parallel factor ``sp``: the TP degree, or 1 without.""" 

65 ccfg.sp = ccfg.t if ccfg.sequence_parallel else 1 

66 

67 

68def derive_expert_degrees(ccfg: Any, strict: bool = True) -> None: 

69 """Set the degrees an expert layer runs with, ``t_exp`` and ``d_exp``. 

70 

71 Args: 

72 ccfg: The config, with its degrees and expert layout set. 

73 strict: Refuse degrees that leave an expert group, the expert width 

74 or the expert count below 1. When false, clamp them to 1 

75 instead, with a warning, so that a search can still reach the 

76 strategy and reject it by memory. 

77 

78 Raises: 

79 TypeError: When *strict* and the degrees cannot hold the experts. 

80 """ 

81 if ccfg.etp > 1: 

82 ccfg.t_exp = ccfg.etp 

83 # d * t = inner dp * outer dp * etp 

84 # inner dp = EP, outer dp = the rest 

85 ccfg.d_exp = ccfg.d * ccfg.t * ccfg.cp // ccfg.t_exp // ccfg.ep 

86 else: 

87 ccfg.t_exp = ccfg.t 

88 if ccfg.d >= ccfg.ep: 

89 ccfg.d_exp = ccfg.d // ccfg.ep 

90 else: 

91 ccfg.d_exp = ccfg.d * ccfg.t // ccfg.ep 

92 if ccfg.t_exp * ccfg.ep > ccfg.d * ccfg.t: 

93 ccfg.t_exp = 1 

94 

95 # The group the runtime forms, which is what every consumer wants; a 

96 # strategy is impossible only when the stage holds fewer ranks than it 

97 # spreads experts over. d_exp stays the older form, and is clamped 

98 # because a group of one still leaves it at zero under CP. 

99 ccfg.edp_group = expert_dp_group(ccfg) 

100 ccfg.d_exp = max(1, ccfg.d_exp) 

101 # A MoE layer dispatches its tokens once and takes them back once. 

102 ccfg.n_dispatch = 1 if ccfg.n_exp > 1 else 0 

103 

104 exp_group1_invalid = ccfg.edp_group < 1 or ccfg.t_exp < 1 

105 exp_group2_invalid = ccfg.hff_exp < 1 or ccfg.n_exp < 1 

106 if not (exp_group1_invalid or exp_group2_invalid): 

107 return 

108 if strict: 

109 raise TypeError( 

110 f"MoE parsing error: edp_group({ccfg.edp_group})/" 

111 f"t_exp({ccfg.t_exp})/" 

112 f"hff_exp({ccfg.hff_exp})/n_exp({ccfg.n_exp})/" 

113 f"DP = {ccfg.d}, TP = {ccfg.t}, EP = {ccfg.ep}, CP = {ccfg.cp}/" 

114 ) 

115 logger.warning( 

116 "Expert degrees invalid for d=%d t=%d ep=%d etp=%d n_exp=%d; clamping to minimum values.", 

117 ccfg.d, ccfg.t, ccfg.ep, ccfg.etp, ccfg.n_exp, 

118 ) 

119 ccfg.d_exp = max(1, ccfg.d_exp) 

120 ccfg.edp_group = max(1, ccfg.edp_group) 

121 ccfg.t_exp = max(1, ccfg.t_exp) 

122 ccfg.hff_exp = max(1, ccfg.hff_exp) 

123 ccfg.n_exp = max(1, ccfg.n_exp) 

124 

125 

126def optimizer_ranks(ccfg: Any) -> int: 

127 """How many data-parallel ranks the optimizer shards a parameter over. 

128 

129 ``os_max_shard`` counts them, as MindSpore's ``optimizer_weight_shard_size`` 

130 and HyperParallel's ``dp_shard`` do, on top of TP's sharding. A count 

131 that does not divide DP shards over all of it, as MindSpore does. 

132 HyperParallel's domain is DP times CP, which its shard spans 

133 (:func:`param_cp`). 

134 """ 

135 domain = ccfg.d * (ccfg.cp if getattr(ccfg, "shard_spans_cp", False) else 1) 

136 ranks = int(ccfg.os_max_shard or 0) 

137 return ranks if ranks >= 1 and domain % ranks == 0 else domain 

138 

139 

140def param_cp(ccfg: Any) -> float: 

141 """What CP's ranks divide a parameter's state by, beyond the shard stated for it. 

142 

143 The legacy schemas shard a parameter, its gradient and its optimizer 

144 states over CP's ranks on top of the optimizer's, and a table they gather 

145 is a CP rank's share of it. HyperParallel's ``dp_shard_size`` already 

146 spans the DP x CP domain, its dense FSDP mesh being 

147 ``(dp_replicate, dp_shard, tp)`` over ``dp * cp`` ranks 

148 (``MeshContext.build_meshs``), and the table it gathers is whole: so 1 

149 there (``shard_spans_cp``), CP elsewhere. 

150 """ 

151 return 1 if getattr(ccfg, "shard_spans_cp", False) else ccfg.cp 

152 

153 

154def expert_dp_group(ccfg: Any) -> int: 

155 """How many ranks hold the same experts. 

156 

157 The runtime spreads the experts over a whole pipeline stage: its expert 

158 mesh is ``(edp_replicate, edp_shard, ep)`` over the ``dp * cp * tp`` 

159 ranks of the device mesh, so ``dp * cp * tp / ep`` ranks hold the same 

160 experts, and an expert tensor shard divides that group again 

161 (``hyper_parallel/distributed/mesh.py``, 

162 ``MeshContext._build_expert_parallel_mesh``). 

163 

164 ``d_exp`` cannot stand in for this group. It is floored by EP against 

165 DP alone, so it loses the remainder when EP does not divide DP and 

166 falls to zero once EP passes DP, which is what context parallelism 

167 does: DP counts the stage's ranks apart from CP and TP, so a strategy 

168 keeps its group while its DP shrinks with CP. Returning zero made a 

169 runnable strategy look invalid, and the consumers that rebuild the 

170 group as ``d_exp * cp * t_exp`` cannot express a group narrower than 

171 CP. 

172 """ 

173 expert_tp = int(ccfg.etp) if ccfg.etp > 1 else 1 

174 return int(ccfg.d * ccfg.t * ccfg.cp) // int(expert_tp * ccfg.ep) 

175 

176 

177def expert_dp_ranks(ccfg: Any) -> int: 

178 """How many ranks hold the same slice of a routed expert, so reduce its gradient together. 

179 

180 The stage's ranks over EP and over the expert's tensor shard 

181 ``t_exp``, whose ranks hold different slices: context parallelism's 

182 ranks hold the same weights and reduce their gradients with the 

183 data-parallel ones, where ``d_exp`` counts the latter alone outside an 

184 expert tensor shard, and :func:`expert_dp_group` counts TP's ranks 

185 whatever slice they hold. 

186 """ 

187 return max(1, int(ccfg.d * ccfg.t * ccfg.cp) // (max(1, int(ccfg.t_exp)) * max(1, int(ccfg.ep)))) 

188 

189 

190def routed_expert_shard(ccfg: Any, ranks: int) -> int: 

191 """How many ranks a routed expert's parameters and optimizer states are sharded over. 

192 

193 A run that states its expert shard, as HyperParallel's ``edp_shard_size``, 

194 shards an expert under expert parallelism over as many ranks of its expert 

195 data-parallel group: the stage's ranks over EP, whatever their DP, CP or 

196 TP, the group of ranks that hold the same experts. Without expert 

197 parallelism its FSDP shards the experts with the other parameters, over 

198 the optimizer's *ranks*. Stated by no one, the optimizer shards them over 

199 the whole group. A run that shards each strategy's experts over its 

200 whole group (``expert_shard_group``) does so under expert parallelism 

201 whatever shard it states, since the group changes with the strategy. 

202 """ 

203 stated = getattr(ccfg, "expert_shard", None) 

204 # The group as the runtime forms it, once a strategy has been laid over 

205 # the config; a dense model and the parser's own doubles state none, and 

206 # keep the older reconstruction from d_exp, which agrees with it 

207 # wherever EP divides DP. 

208 whole = max(1, int(ccfg.d * ccfg.cp * ccfg.t // max(1, int(ccfg.ep)))) 

209 group = max(1, int(ccfg.edp_group)) if ccfg.edp_group else whole 

210 if getattr(ccfg, "expert_shard_group", False) and ccfg.ep > 1: 

211 return group 

212 if stated is None: 

213 if not ccfg.has_op: 

214 return param_cp(ccfg) * ccfg.t_exp 

215 return group if ccfg.edp_group else ccfg.d_exp * ccfg.cp * ccfg.t_exp 

216 if ccfg.ep > 1: 

217 return math.gcd(int(stated), group) 

218 return ranks * param_cp(ccfg) * ccfg.t_exp 

219 

220 

221def derive_optimizer_sharding(ccfg: Any) -> None: 

222 """Set how parameters, optimizer states and gradients are sharded. 

223 

224 With optimizer sharding, a parameter is sharded over TP and then over 

225 :func:`optimizer_ranks` data-parallel ranks, and a routed expert as 

226 :func:`routed_expert_shard` says. Gradients are sharded as 

227 the parameters are when the run says so (``grad_shard_as_params``), as 

228 FSDP shards them; else over the whole optimizer shard when the run 

229 shards them (``has_grad_shard``), and over TP alone otherwise. 

230 """ 

231 ranks = optimizer_ranks(ccfg) if ccfg.has_op else 1 

232 # Non expert params, sharded over CP's ranks too where the stated shard 

233 # does not span them already (param_cp) 

234 ccfg.shard_p_os_non_exp_partial = ranks * ccfg.t * param_cp(ccfg) 

235 ccfg.shard_p_os_non_exp = ( 

236 (ccfg.d if ccfg.has_op else 1) * ccfg.cp * ccfg.t 

237 ) 

238 

239 # Expert params 

240 ccfg.shard_p_os_exp_partial = math.gcd(ccfg.n_exp, ranks * ccfg.t_exp) 

241 ccfg.shard_p_os_exp = routed_expert_shard(ccfg, ranks) 

242 

243 # Gradients 

244 if getattr(ccfg, "grad_shard_as_params", False): 

245 grads = (ccfg.shard_p_os_non_exp_partial, ccfg.shard_p_os_exp, ccfg.shard_p_os_exp_partial) 

246 elif ccfg.has_grad_shard: 

247 grads = (ccfg.shard_p_os_non_exp, ccfg.shard_p_os_exp, ccfg.shard_p_os_exp_partial) 

248 else: 

249 grads = (ccfg.t, ccfg.t_exp, ccfg.t_exp) 

250 ccfg.shard_grad_non_exp, ccfg.shard_grad_exp, ccfg.shard_grad_exp_partial = grads 

251 

252 

253def derive_param_casts(ccfg: Any) -> None: 

254 """Set whether a layer keeps the weight casts the formulas price beside its matmuls, ``keeps_param_casts``. 

255 

256 The run states it as ``param_casts``; unstated, a layer keeps them where 

257 the optimizer does not shard, the formulas' convention. 

258 """ 

259 stated = getattr(ccfg, "param_casts", None) 

260 ccfg.keeps_param_casts = (not ccfg.has_op) if stated is None else bool(stated) 

261 

262 

263def derive_comm_flags(ccfg: Any) -> None: 

264 """Set the communication factors and the transitional overlaps.""" 

265 ccfg.comm_d_non_exp = ( 

266 0 

267 if ((ccfg.d == 1) or not ccfg.has_op) 

268 else (2 if not ccfg.has_grad_shard else 3) 

269 ) # data parallel comm factor 

270 # Whether another rank holds the same slice of a routed expert, as a 

271 # shard or a copy, so that its gradient is reduced: d_exp left CP's 

272 # ranks out and read 1 where they reduce it. 

273 ccfg.comm_d_exp = ( 

274 0 

275 if ((expert_dp_ranks(ccfg) == 1) or not ccfg.has_op) 

276 else (2 if not ccfg.has_grad_shard else 3) 

277 ) # data parallel comm factor 

278 ccfg.comm_t = float(ccfg.t > 1) # tensor parallel comm factor 

279 ccfg.comm_ep = float( 

280 ccfg.ep > 1 or ccfg.n_exp > 1 

281 ) # expert parallel comm factor 

282 ccfg.comm_cp = float(ccfg.cp > 1) # context parallel comm factor 

283 ccfg.comm_dp_overlap = 0.9 # transitional overlap, see _cost_model_variables.py 

284 ccfg.comm_tp_overlap = 0.5 # transitional overlap, see _cost_model_variables.py 

285 

286 

287def derive_embedding_sharding(ccfg: Any) -> None: 

288 """Set how the embedding table is sharded, ``shard_embed``, and over how many of those 

289 ranks it is gathered to compute with it, ``gather_embed``. 

290 

291 The table is split over tensor parallelism unless the vocabulary 

292 embedding runs data parallel without pipelining, and over data 

293 parallelism unless the config says it is not. FSDP, which shards the 

294 gradients as the parameters (``grad_shard_as_params``), shards the 

295 table as every parameter, over :func:`optimizer_ranks`, its FSDP group 

296 and not the whole of data parallelism under HSDP, and gathers it over 

297 them to compute with it. 

298 """ 

299 tp = 1 if (ccfg.vocab_emb_dp and ccfg.p == 1) else ccfg.t 

300 fsdp = getattr(ccfg, "grad_shard_as_params", False) 

301 ranks = (optimizer_ranks(ccfg) if ccfg.has_op else 1) if fsdp else ccfg.d 

302 ccfg.shard_embed = (ranks if ccfg.emb_dp_sharded else 1) * tp 

303 ccfg.gather_embed = ranks if fsdp and ccfg.emb_dp_sharded else 1 

304 

305 

306def hyper_rec_op(selective: Union[bool, list]) -> dict[str, int]: 

307 """Recompute switches for a HyperParallel activation checkpoint mode. 

308 

309 Args: 

310 selective: The parsed ``sel_rec``: truthy when the run uses 

311 selective activation checkpointing. 

312 

313 Returns: 

314 The switches for ``ccfg.rec_op``: :data:`HYPER_SELECTIVE_REC_OP` 

315 for a selective run, and every op kept otherwise. 

316 """ 

317 if selective: 

318 return dict(HYPER_SELECTIVE_REC_OP) 

319 return dict.fromkeys(HYPER_SELECTIVE_REC_OP, 1) 

320 

321 

322def mindformers_rec_op(ccfg: Any, selective: Any = None) -> dict[str, int]: 

323 """Recompute switches of MindFormers' selective recompute. 

324 

325 What ``select_recompute`` recomputes depends on flash attention and 

326 sequence parallelism; ``select_comm_recompute`` recomputes the 

327 sequence-parallel all-gather. 

328 

329 Args: 

330 ccfg: The config, with ``sel_rec``, ``sel_comm_rec``, ``has_fa`` and 

331 ``sp`` set. 

332 selective: Whether the run recomputes selectively, ``sel_rec`` by 

333 default. 

334 

335 Returns: 

336 The switches for ``ccfg.rec_op``: 1 keeps the op's activation and 0 

337 recomputes it. 

338 """ 

339 selective = ccfg.sel_rec if selective is None else selective 

340 return { 

341 "attBMM": int(not (selective and not ccfg.has_fa and ccfg.sp > 1)), 

342 "headCast": int(not (selective and ccfg.has_fa)), 

343 "dropout": 1, 

344 "softmax": int(not (selective and not ccfg.has_fa)), 

345 "normOp": int(not (selective and ccfg.sp > 1)), 

346 "gather": int(not (ccfg.sel_comm_rec and ccfg.sp > 1)), 

347 "ffAct": int(not (selective and ccfg.sp > 1)), 

348 } 

349 

350 

351def derive_recompute_ranges(ccfg: Any) -> None: 

352 """Check the recompute ranges a config states against its layers. 

353 

354 Raises: 

355 ValueError: When a range reaches past the model's last layer. 

356 """ 

357 ranges = stated_recompute(ccfg) 

358 if not ranges: 

359 return 

360 layers = int(ccfg.n_lay + ccfg.n_mtp) 

361 for item in ranges: 

362 last = item.first + (item.count or 1) - 1 

363 if last >= layers: 

364 raise ValueError( 

365 f"{ccfg.model_name}: recompute range {item.to_dict()} reaches layer {last}, " 

366 f"past the model's {layers} layers" 

367 ) 

368 

369 

370def derive_recompute_switches(ccfg: Any) -> None: 

371 """Set which activations a recomputed layer keeps: ``rec_op``, and each layer's own, ``layer_switches``. 

372 

373 ``sel_rec_rule`` names the framework whose selective recompute the run 

374 uses: HyperParallel's recomputes a fixed set of ops, MindFormers' a set 

375 that depends on flash attention and sequence parallelism. A selective 

376 recompute range that states its ops sets them instead. Where the 

377 ranges state several selective settings, ``layer_switches`` gives each 

378 layer in model order its range's, None for a layer that is not 

379 selective, and the estimates read each layer's through its context, 

380 the per-layer channel; ``rec_op`` holds the first range's. Otherwise 

381 ``layer_switches`` is None and ``rec_op`` holds the one setting. 

382 

383 Raises: 

384 ValueError: When the config names no known rule, or it is a 

385 multimodal config whose ranges state several selective 

386 settings. 

387 """ 

388 ranges = stated_recompute(ccfg) 

389 selective = [item for item in ranges or () if item.option == "selective"] 

390 run_selective = ccfg.sel_rec if ranges is None else bool(selective) 

391 if ccfg.sel_rec_rule == "hyperparallel": 

392 switches = hyper_rec_op(run_selective) 

393 elif ccfg.sel_rec_rule == "mindformers": 

394 switches = mindformers_rec_op(ccfg, run_selective) 

395 else: 

396 raise ValueError(f"Unknown selective recompute rule {ccfg.sel_rec_rule!r}") 

397 # Every switch, as a range's own state them: one the rule leaves out keeps its op. 

398 switches = {op: switches.get(op, 1) for op in RECOMPUTE_OPS} 

399 settings = {tuple(sorted((item.switches() or switches).items())) for item in selective} 

400 ccfg.layer_switches = None 

401 if len(settings) > 1: 

402 if getattr(ccfg, "multimodal", False): 

403 raise ValueError( 

404 f"{ccfg.model_name}: its recompute ranges state {len(settings)} selective settings; " 

405 "a multimodal config prices one" 

406 ) 

407 ccfg.layer_switches = _per_layer_switches(ccfg, selective, switches) 

408 switches = dict(selective[0].switches() or switches) 

409 elif settings: 

410 switches = dict(next(iter(settings))) 

411 ccfg.rec_op = Config(switches) 

412 

413 

414def _per_layer_switches(ccfg: Any, selective: list, rule: Dict[str, int]) -> tuple: 

415 """Each layer's switches in model order, its selective range's or else *rule*'s; None where none covers it.""" 

416 layers = int(ccfg.n_lay + ccfg.n_mtp) 

417 per_layer = [None] * layers 

418 for item in selective: 

419 stop = layers if item.count is None else min(layers, item.first + item.count) 

420 per_layer[item.first:stop] = [dict(item.switches() or rule)] * max(0, stop - item.first) 

421 return tuple(per_layer) 

422 

423 

424def derive_flash_attention_factor(ccfg: Any) -> None: 

425 """Set the flash attention factor, ``s_fa``. 

426 

427 The attention scores are priced over ``s / a`` rather than ``s`` when 

428 flash attention is on. 

429 """ 

430 ccfg.s_fa = ccfg.s / ccfg.a if ccfg.has_fa and ccfg.a > 0 else ccfg.s 

431 

432 

433def family_run(ccfg: Any) -> Dict[str, Any]: 

434 """Return the run defaults of the config's family, from its op profile. 

435 

436 A vision tower takes the vision profile's, but for its activation 

437 sharding, which is its language model's family's. 

438 """ 

439 arch = getattr(ccfg, "arch", None) 

440 run = dict(family_profile(arch).run) 

441 inherited = getattr(ccfg, "inherited_arch", None) 

442 if arch == VISION_ARCH and inherited is not None: 

443 run["shard_activations"] = family_profile(inherited).run["shard_activations"] 

444 return run 

445 

446 

447def _stated(ccfg: Any, name: str, run: Mapping[str, Any]) -> Any: 

448 """Return the run fact *name* the config states, else its family's.""" 

449 value = getattr(ccfg, name, None) 

450 return run[name] if value is None else value 

451 

452 

453def derive_byte_widths(ccfg: Any, run: Mapping[str, Any]) -> None: 

454 """Set the byte widths the estimators read from those the run states, else its family's. 

455 

456 Gradients take memory only under pipeline parallelism, unless the run 

457 accumulates them without it too (``grad_accumulation``, which it gives as 

458 ``accumulates_grads``: the search then gives PP 1 several micro-batches, 

459 its accumulation steps). The optimizer 

460 keeps, per parameter, its states and any copy of the parameters: a 

461 layer's parameter as many states as its optimizer has, the embedding 

462 and output tables' AdamW's two. 

463 """ 

464 ccfg.accumulates_grads = bool(_stated(ccfg, "grad_accumulation", run)) 

465 accumulates = ccfg.p > 1 or ccfg.accumulates_grads 

466 ccfg.bytes_grad = _stated(ccfg, "grad_bytes", run) if accumulates else 0 

467 ccfg.bytes_os = _stated(ccfg, "optimizer_state_bytes", run) 

468 main_copy = _stated(ccfg, "main_param_bytes", run) 

469 ccfg.bytes_optim = _stated(ccfg, "optimizer_states", run) * ccfg.bytes_os + main_copy 

470 ccfg.bytes_optim_table = 2 * ccfg.bytes_os + main_copy 

471 ccfg.bytes_norm = _stated(ccfg, "norm_bytes", run) 

472 ccfg.bytes_dropout = _stated(ccfg, "dropout_bytes", run) 

473 

474 

475def derive_resharding(ccfg: Any, run: Mapping[str, Any]) -> None: 

476 """Set whether FSDP frees a layer's gathered parameters once it has run, ``reshards``, 

477 whether it holds each layer's reduce-scatter output until the backward ends, 

478 ``defers_grads``, and whether it reduces a layer's gradients while the next 

479 layer's backward runs, ``overlaps_grad_reduce``.""" 

480 ccfg.reshards = bool(_stated(ccfg, "reshard_params", run)) 

481 ccfg.defers_grads = bool(_stated(ccfg, "deferred_grad_accumulation", run)) 

482 ccfg.overlaps_grad_reduce = bool(_stated(ccfg, "overlapped_grad_reduce", run)) 

483 

484 

485def derive_activation_sharding(ccfg: Any, run: Mapping[str, Any]) -> None: 

486 """Set how tensor parallelism shards what a layer keeps. 

487 

488 ``shard_recompute_input`` divides the input a fully recomputed layer 

489 keeps, and ``shard_output_activ`` the output layer's activations. Both 

490 are the TP degree when the run shards the activations between layers 

491 (``shard_activations``), and 1 otherwise; a recomputed layer's input is 

492 sliced over TP too when the run's recompute slices it, and the output 

493 layer's activations only where the loss runs on logits sharded over the 

494 vocabulary (``loss_parallel``, which derive gives as ``shards_logits``): 

495 otherwise every rank gathers them whole. 

496 """ 

497 sharded = _stated(ccfg, "shard_activations", run) 

498 sliced = sharded or getattr(ccfg, "recompute_slice_activation", False) 

499 ccfg.shards_logits = bool(_stated(ccfg, "loss_parallel", run)) 

500 ccfg.shard_recompute_input = ccfg.t if sliced else 1 

501 ccfg.shard_output_activ = ccfg.t if sharded and ccfg.shards_logits else 1 

502 

503 

504def derive_qk_norm(ccfg: Any) -> None: 

505 """Count the QK-norm each layer runs, ``n_qknorm``: one where the model has one. 

506 

507 A layer kind whose attention has none, a linear one, states 0 instead. 

508 """ 

509 ccfg.n_qknorm = 1 if getattr(ccfg, "qk_norm", False) else 0 

510 

511 

512def derive_head_dim(ccfg: Any) -> None: 

513 """Price an MLA family's heads at the value heads' width, ``dh``. 

514 

515 The model's ``v_head_dim``, else its family's. A family whose profile 

516 states no value-head width keeps the head width its parser read. 

517 """ 

518 width = family_profile(getattr(ccfg, "arch", None)).model.get("v_head_dim") 

519 if width is not None: 

520 ccfg.dh = getattr(ccfg, "v_head_dim", None) or width 

521 

522 

523def derive_family(ccfg: Any) -> None: 

524 """Set what the config's family decides where its producer states nothing. 

525 

526 The byte widths, the resharding and the activation sharding, from the 

527 run facts the config states and else its family's (:func:`family_run`), 

528 and an MLA family's head width. 

529 """ 

530 run = family_run(ccfg) 

531 derive_byte_widths(ccfg, run) 

532 derive_resharding(ccfg, run) 

533 derive_activation_sharding(ccfg, run) 

534 derive_head_dim(ccfg) 

535 

536 

537def derive(ccfg: Any, strict: bool = True) -> None: 

538 """Compute the config's derived fields from its primary ones. 

539 

540 Args: 

541 ccfg: The config, with its model, strategy and run facts set. 

542 strict: As in :func:`derive_expert_degrees`. 

543 

544 Raises: 

545 TypeError: When *strict* and the degrees cannot hold the experts. 

546 ValueError: When the config names no known selective recompute rule. 

547 """ 

548 derive_sequence_parallel(ccfg) 

549 derive_expert_degrees(ccfg, strict) 

550 derive_optimizer_sharding(ccfg) 

551 derive_param_casts(ccfg) 

552 derive_comm_flags(ccfg) 

553 derive_embedding_sharding(ccfg) 

554 derive_recompute_ranges(ccfg) 

555 derive_recompute_switches(ccfg) 

556 derive_flash_attention_factor(ccfg) 

557 derive_qk_norm(ccfg) 

558 derive_family(ccfg)