Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / auto_parallel / sapp_nd / memory_estimation / _backbone.py: 60%

402 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-07-06 05:41 +0800

1# Copyright 2025 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"""backbone memory estimation module""" 

16from __future__ import annotations 

17from typing import TYPE_CHECKING 

18import os 

19import ast 

20import math 

21import logging 

22import inspect 

23import pprint 

24import importlib 

25import matplotlib.pyplot as plt 

26from PIL import Image 

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

28from hyper_parallel.auto_parallel.sapp_nd.nd.logger import logger as nd_logger 

29from hyper_parallel.auto_parallel.sapp_nd.nd.common.cost_model_preprocess import CostModelConfig 

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

31from hyper_parallel.auto_parallel.sapp_nd.memory_estimation._context import Context, MemType 

32from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.evaluators.utils import EvalUtils 

33from hyper_parallel.auto_parallel.sapp_nd.memory_estimation._bwd_overhead import _BackwardOverhead 

34from hyper_parallel.auto_parallel.sapp_nd.memory_estimation._ppb import _PPB 

35from hyper_parallel.auto_parallel.sapp_nd.memory_estimation.hook_base import MemEvalHook 

36 

37if TYPE_CHECKING: 

38 from typing import Any, Dict, Tuple 

39 

40current_dir = os.path.dirname(os.path.abspath(__file__)) 

41EVAL_YML = os.path.join(current_dir, "configs_eval/default.yaml") 

42 

43 

44class _Backbone: 

45 """backbone class""" 

46 

47 def __init__(self, config: Any, **kwargs): 

48 self._child_cls = self 

49 self.mb = EvalUtils.mb 

50 self.eval_cfg = Config(kwargs.get("eval_yml", EVAL_YML)) 

51 self._ctx = kwargs.get("ctx", Context()) 

52 self._ccfg = kwargs.get("ccfg", None) 

53 self.framework = kwargs.get("framework", None) 

54 self.source_code = kwargs.get("source_code", None) 

55 self.hook_cls, self.config_path = None, None 

56 if not self._ccfg: 

57 self.hook_cls = kwargs.get("hook_cls", self.eval_cfg.hook_class) 

58 if isinstance(self.hook_cls, str): 

59 self._load_eval_yaml_hook_cls(self.hook_cls) 

60 if self.hook_cls and not isinstance(self.hook_cls, MemEvalHook): 

61 raise AttributeError( 

62 f"'{self.hook_cls}' is not a MemEvalHook instance" 

63 ) 

64 if config: 

65 if kwargs.get("log_level", 1) == 0: 

66 # logger.setLevel(logging.CRITICAL) 

67 nd_logger.setLevel(logging.CRITICAL) 

68 self.update_config(config) 

69 else: 

70 raise AttributeError("missing config") 

71 self.evaluator_instances = None 

72 self.ppb = None 

73 self._overhead_obj = _BackwardOverhead( 

74 self, self._ccfg, self._ctx, self._inner_dynamic_mem 

75 ) 

76 self._ppb_obj = _PPB(self.eval_cfg, self._inner_dynamic_mem) 

77 

78 @property 

79 def ccfg(self) -> CostModelConfig: 

80 """read-only""" 

81 return self._ccfg 

82 

83 @property 

84 def ctx(self) -> Context: 

85 """read-only""" 

86 return self._ctx 

87 

88 def _load_eval_yaml_hook_cls(self, hook_cls): 

89 """hook_class in eval yaml""" 

90 target_mod_path = None 

91 try: 

92 # search in folder 'hooks' 

93 hooks_dir = os.path.join(current_dir, "hooks") 

94 for f in os.listdir(hooks_dir): 

95 if f.endswith(".py"): 

96 mod_path = f"hyper_parallel.auto_parallel.sapp_nd.memory_estimation.hooks.{f.split('.')[0]}" 

97 spec = importlib.util.find_spec(mod_path) 

98 if spec is None or spec.origin is None: 

99 continue 

100 with open(spec.origin, "r", encoding="utf-8") as mf: 

101 source = mf.read() 

102 tree = ast.parse(source) 

103 mod_cls = None 

104 for node in ast.walk(tree): 

105 if isinstance(node, ast.ClassDef) and node.name == hook_cls: 

106 mod_cls = node 

107 break 

108 if mod_cls: 

109 target_mod_path = mod_path 

110 break 

111 if target_mod_path: 

112 module = importlib.import_module(target_mod_path) 

113 self.hook_cls = getattr(module, hook_cls)() 

114 except (ModuleNotFoundError, ImportError) as e: 

115 print(e) 

116 

117 # Peak memory estimation 

118 

119 def update_config(self, new_config: Any) -> None: 

120 """processing input config""" 

121 if self._ccfg is not None: 

122 self._ccfg.update_config(new_config, self.hook_cls, self.framework, self.source_code) 

123 else: 

124 self._ccfg = CostModelConfig(new_config, self.hook_cls, self.framework, self.source_code) 

125 if isinstance(new_config, str): 

126 if not self.config_path: 

127 logger.info( 

128 "%s Process config file: %s", 

129 "=" * 30, 

130 new_config.split("/")[-1], 

131 ) 

132 self.config_path = new_config 

133 self.evaluator_instances = None 

134 self.ppb = None 

135 

136 def _inner_static_mem(self) -> float: 

137 """static memory evaluation for backbone estimation""" 

138 if self._ctx.current_node in self._ctx.node_eval: 

139 p = self._ctx.eval.stat.p(self._ccfg, self._ctx) 

140 ost = self._ctx.eval.stat.os(self._ccfg, self._ctx) 

141 grad = self._ctx.eval.stat.grad(self._ccfg, self._ctx) 

142 res = p + ost + grad 

143 self._ctx.save2log("_param", res) 

144 # Log routed/shared expert param breakdown for MoE layers 

145 if self._ccfg.n_exp > 1 and self.is_regular_layer(self._ctx.current_node): 

146 result = self._ctx.eval.num_p(self._ccfg, self._ctx) 

147 if isinstance(result, tuple) and len(result) == 3: 

148 _, routed_exp_p, shared_exp_p = result 

149 routed_exp_mem = ( 

150 routed_exp_p / self._ccfg.ep 

151 * self._ccfg.bytes_p / self._ccfg.shard_p_os_exp 

152 ) 

153 shared_exp_mem = ( 

154 shared_exp_p 

155 * self._ccfg.bytes_p / self._ccfg.shard_p_os_exp_partial 

156 ) 

157 self._ctx.save2log("routed_exp_param", routed_exp_mem) 

158 self._ctx.save2log("shared_exp_param", shared_exp_mem) 

159 return p + ost + grad 

160 return 0 

161 

162 def _inner_dynamic_mem( 

163 self, ppb=False, default_micro_factor=None 

164 ) -> tuple[float, float]: 

165 """dynamic memory evaluation for backbone estimation""" 

166 if self._ctx.current_node in self._ctx.node_eval: 

167 if ppb: 

168 micro_factor = 1 

169 elif default_micro_factor: 

170 micro_factor = default_micro_factor 

171 else: 

172 sched = self._ccfg.pp_sched 

173 micro_factor = self._ctx.pp_micro_eval[sched]( 

174 self._ccfg, self._ctx 

175 ) 

176 self._ctx.micro_factor = max(1, micro_factor) 

177 activation = self._ctx.eval.dyn.activation(self._ccfg, self._ctx) 

178 self._ctx.save2log("_activ", activation) 

179 comm = self._inner_comm_mem(micro_factor) 

180 return activation, comm 

181 return 0, 0 

182 

183 def _inner_comm_mem(self, micro_factor) -> float: 

184 """dynamic memory evaluation for backbone estimation""" 

185 if self._ctx.current_node in self._ctx.node_eval: 

186 comm_eval_field = self._ctx.eval.dyn.comm 

187 comm_cat = { 

188 "dp": MemType.AG_COMM, 

189 "tp": MemType.AG_COMM, 

190 "cp": MemType.AG_COMM, 

191 "ep": MemType.A2A_COMM, 

192 } 

193 comm_mem = {} 

194 for k, fun in vars(comm_eval_field).items(): 

195 if fun is None: 

196 continue 

197 sig_param = inspect.signature(fun).parameters.values() 

198 if ( 

199 # any( 

200 # p.kind == inspect.Parameter.VAR_KEYWORD 

201 # for p in sig_param 

202 # ) 

203 # and len(sig_param) > 2 

204 len(sig_param) > 2 

205 ): 

206 comm = fun(self._ccfg, self._ctx, micro_factor) 

207 else: 

208 comm = fun(self._ccfg, self._ctx) 

209 comm_mem[k] = comm 

210 res = EvalUtils.eval_expr_insight( 

211 expr=self._ctx.comm_expr, 

212 ctx=self._ctx, 

213 mem_val=comm_mem, 

214 mem_cat=comm_cat, 

215 ) 

216 self._ctx.save2log("_comm", res) 

217 return res 

218 return 0 

219 

220 def __subplot_stages(self, stage_i, stat_mem_i, dyn_mem_i, i) -> None: 

221 """Plot static and dynamic memory for one pipeline stage.""" 

222 _, ax = plt.subplots(figsize=(5, 8)) 

223 bottoms = {"stat": 0, "dyn": 0} 

224 color_stat = plt.get_cmap("cividis") 

225 color_dyn = plt.get_cmap("plasma") 

226 total_lay = sum(len(chunk) for chunk in stage_i) 

227 color_denominator = max(1, total_lay - 1) 

228 for chunk_id, chunk in enumerate(stage_i): 

229 for lay_id, lay_type in enumerate(chunk): 

230 real_id = self._ctx.real_lay_ids[chunk_id][i][lay_id] 

231 name = f"Lay_{real_id}_{lay_type.name[0]}" 

232 idx = chunk_id * len(dyn_mem_i) + lay_id 

233 stat = self.mb(stat_mem_i[chunk_id][lay_id]) 

234 dyn = self.mb(dyn_mem_i[chunk_id][lay_id]) 

235 ax.bar( 

236 0.1, 

237 [dyn], 

238 bottom=bottoms["dyn"], 

239 label=name, 

240 color=color_dyn(idx / color_denominator), 

241 width=0.15, 

242 linewidth=0.5, 

243 edgecolor="black", 

244 ) 

245 ax.text( 

246 0.2, 

247 bottoms["dyn"] + dyn / 2, 

248 f"DYN_{name}", 

249 va="center", 

250 fontsize=5, 

251 ) 

252 ax.bar( 

253 0.5, 

254 [stat], 

255 bottom=bottoms["stat"], 

256 label=name, 

257 color=color_stat(idx / color_denominator), 

258 width=0.15, 

259 linewidth=0.5, 

260 edgecolor="black", 

261 ) 

262 ax.text( 

263 0.6, 

264 bottoms["stat"] + stat / 2, 

265 f"STAT_{name}", 

266 va="center", 

267 fontsize=5, 

268 ) 

269 bottoms["dyn"] += dyn 

270 bottoms["stat"] += stat 

271 ax.axhline( 

272 self.get_max_device_memory(), 

273 color="red", 

274 linewidth=1, 

275 ls="dotted", 

276 ) 

277 ax.axhline( 

278 bottoms["dyn"] + bottoms["stat"], 

279 color="blue", 

280 linewidth=1, 

281 ls="dashed", 

282 ) 

283 ax.text( 

284 0, 

285 self.get_max_device_memory(), 

286 "Device Memory", 

287 color="red", 

288 va="bottom", 

289 fontsize=8, 

290 ) 

291 ax.text( 

292 0.62, 

293 bottoms["dyn"] + bottoms["stat"], 

294 "Prediction total", 

295 color="blue", 

296 va="bottom", 

297 fontsize=8, 

298 ) 

299 ax.set_xticks([]) 

300 ax.set_ylabel("Size (MB)") 

301 ax.set_xlim([0, 0.8]) 

302 ax.set_xlabel(f"Stage_{i}") 

303 

304 def __plot_stages(self, stages, stat_mems, dyn_mems) -> None: 

305 """plot bars for estimations""" 

306 

307 logger.info("Plotting predictions in plots/") 

308 if not os.path.exists("plots"): 

309 os.makedirs("plots") 

310 imgs = [] 

311 for stage_id in range(self._ccfg.p): 

312 self.__subplot_stages( 

313 stages[stage_id], 

314 stat_mems[stage_id], 

315 dyn_mems[stage_id], 

316 stage_id, 

317 ) 

318 img = f"plots/MemPlot_stage_{stage_id}.png" 

319 imgs += [(stage_id, img)] 

320 logger.info("save plot: %s", img) 

321 plt.savefig(img, dpi=300, bbox_inches="tight") 

322 plt.clf() 

323 plt.close() 

324 # Concatenate every stage plots 

325 if imgs: 

326 imgs = sorted(imgs) 

327 canvas = [Image.open(i) for _, i in imgs] 

328 stage_canva = Image.new( 

329 "RGB", 

330 ( 

331 canvas[0].size[0] 

332 * 2 ** math.ceil(math.log2(len(canvas)) / 2), 

333 canvas[0].size[1] 

334 * 2 ** math.floor(math.log2(len(canvas)) / 2), 

335 ), 

336 ) 

337 offset_x, offset_y = 0, 0 

338 for i in canvas: 

339 stage_canva.paste(i, (offset_x, offset_y)) 

340 offset_x += i.size[0] 

341 if offset_x >= stage_canva.size[0]: 

342 offset_x = 0 

343 offset_y += i.size[1] 

344 stage_canva.save("plots/MemPlot_all_stages.png") 

345 logger.info("save stage plot: plots/MemPlot_all_stages.png") 

346 

347 def __update_stage_logs(self, stage_logs: list, stage_id: int) -> None: 

348 """update stage's temporary buffers from ctx's buffers""" 

349 if not stage_logs[stage_id].node_compute_log: 

350 stage_logs[stage_id].node_compute_log = {} 

351 stage_logs[stage_id].node_compute_log.update( 

352 self._ctx.node_compute_log 

353 ) 

354 if not stage_logs[stage_id].accu_mem_type: 

355 stage_logs[stage_id].accu_mem_type = { 

356 mt: 0 for mt in list(MemType) 

357 } 

358 for mem_type in list(MemType): 

359 val = self._ctx.accu_mem_type[mem_type] 

360 stage_logs[stage_id].accu_mem_type[mem_type] += val 

361 

362 def __preprocess_layer_custom_config_list(self, stages: list) -> list: 

363 """flatten layer_custom_config for backbone estimation""" 

364 flatten = sum( 

365 [[f[1]] * f[0] for f in self._ccfg.layer_custom_config], [] 

366 ) 

367 total_n_lay = self._ccfg.n_lay + self._ccfg.n_mtp 

368 total_n_lay_stages = self._ccfg.count_layers(stages) 

369 if not self._ccfg.multimodal and total_n_lay != total_n_lay_stages: 

370 raise(AttributeError( 

371 f"Mismatch of num_layers between parsed value ({total_n_lay})" 

372 f" and generated partitions ({total_n_lay_stages})" 

373 f" => offset may be incorrect ({self._ccfg.offset})" 

374 )) 

375 if self._ccfg.n_lay > 0 and len(flatten) != total_n_lay: 

376 raise AttributeError( 

377 f"layer_custom_config occurrences ({len(flatten)})" 

378 f" != num_layers ({total_n_lay})" 

379 ) 

380 if self._ccfg.pp_sched == "zero_bubble_v": 

381 n_layer_first_chunk = ( 

382 sum(len(s[0]) for s in stages) - 1 

383 ) # Except embedding layer 

384 flatten = ( 

385 flatten[:n_layer_first_chunk] 

386 + flatten[n_layer_first_chunk:][::-1] 

387 ) 

388 return flatten 

389 

390 def _estimate_backbone(self, *args) -> Tuple[list, Dict]: 

391 """Evaluator's main function for estimation""" 

392 stages = args[0] 

393 spec_stage_id = args[3] 

394 

395 if spec_stage_id >= self._ccfg.p or spec_stage_id < 0: 

396 spec_stage_id = -1 

397 # Process partition generation 

398 if not stages: 

399 stages = self._ccfg.generate_partitions_vpp() 

400 # multimodal=self._ccfg.multimodal 

401 # ) 

402 if not self._ccfg.multimodal: 

403 return self.__estimate_stages_backbone( 

404 stages, args[1], args[2], spec_stage_id, args[4] 

405 ) 

406 res = [] 

407 original_ccfg = self._ccfg 

408 common_lc = [] 

409 self.evaluator_instances = [] 

410 # Build common layer_custom_config + Build temporary evaluators 

411 for m in self._ccfg.mm_order: 

412 self._ccfg.mm_ccfgs[m].config = original_ccfg.config 

413 if not self._child_cls: 

414 raise AttributeError("expected non null _child_cls") 

415 tmp_evaluator = type(self._child_cls)( 

416 None, 

417 ccfg=self._ccfg.mm_ccfgs[m], 

418 trace_fun=self.toggle_func_trace 

419 ) 

420 tmp_evaluator.import_eval_yaml() 

421 num_layer = tmp_evaluator.get_num_layers() 

422 # assert not isinstance(num_layer, tuple) 

423 strategy = tmp_evaluator.get_strategy() 

424 full_rec = strategy["full_rec"] 

425 offset = strategy["offset"] 

426 self._ccfg.hooks_dict[m](tmp_evaluator) 

427 strategy = tmp_evaluator.get_strategy() 

428 if ( 

429 tmp_evaluator.get_num_layers() != num_layer 

430 or strategy["full_rec"] != full_rec 

431 or strategy["offset"] != offset 

432 ): 

433 # Reverify num layers, recomp, offset after hook 

434 stages[m] = ( 

435 tmp_evaluator.ccfg.generate_partitions_vpp_unimodal() 

436 ) 

437 tmp_evaluator.set_layer_custom(None) 

438 common_lc += self._ccfg.mm_ccfgs[m].layer_custom_config 

439 self.evaluator_instances += [tmp_evaluator] 

440 if args[1]: 

441 logger.info("Submodule %s", self._ccfg.mm_ccfgs[m].model_name) 

442 tmp_evaluator.print_ctx() 

443 self.print_stages(stages[m]) 

444 self.set_layer_custom(common_lc) 

445 if args[1]: 

446 logger.info( 

447 "Combined layer_custom_config for %s\n%s", 

448 self._ccfg.model_name, 

449 pprint.pformat(self._ccfg.layer_custom_config, compact=True), 

450 ) 

451 logger.info( 

452 "Sub evaluator instances for %s\n%s", 

453 self._ccfg.model_name, 

454 pprint.pformat(self.evaluator_instances, compact=True), 

455 ) 

456 

457 res = self.__estimate_stages_backbone( 

458 self._ccfg.combine_partition_multimodal(stages), 

459 args[1], 

460 args[2], 

461 spec_stage_id, 

462 args[4], 

463 ) 

464 self._ccfg = original_ccfg 

465 return res 

466 

467 def __estimate_stages_backbone(self, *args) -> Tuple[list, Dict]: 

468 """Evaluator's main function for stage estimation""" 

469 stages = args[0] 

470 verbose = args[1] 

471 compute_ppb = args[2] 

472 spec_stage_id = args[3] 

473 

474 if verbose: 

475 logger.info("Partition of layers :") 

476 self._ccfg.print_stages(stages, spec_stage_id) 

477 insights = [] 

478 # Compute peak memory 

479 flatten = self.__preprocess_layer_custom_config_list(stages) 

480 if verbose: 

481 logger.info( 

482 "Flatten layer_custom_config\n%s", 

483 pprint.pformat( 

484 [f if not f else f.__name__ for f in flatten], compact=True 

485 ), 

486 ) 

487 

488 stage_misc = { 

489 "stat": [[[0 for _ in c] for c in s] for s in stages], 

490 "dyn": [[[0 for _ in c] for c in s] for s in stages], 

491 "logs": [Config({}) for _ in range(self._ccfg.p)], 

492 } 

493 # tmp_ppb_lay_desc = [] # PPB purpose 

494 ppb_lay_desc = [] 

495 record_lay_types = {} 

496 self.__chunk_stage_lay_loops( 

497 flatten, 

498 stages, 

499 record_lay_types, 

500 stage_misc, 

501 verbose, 

502 compute_ppb, 

503 ppb_lay_desc, # tmp_ppb_lay_desc, 

504 ) 

505 self.__postprocess_stages( 

506 stages, 

507 record_lay_types, 

508 stage_misc, 

509 verbose, 

510 spec_stage_id, 

511 insights, 

512 ) 

513 # PPB Input 

514 ppb_input = None 

515 if compute_ppb == 1: 

516 self._ppb_obj.ppb_combine_bodies(ppb_lay_desc) 

517 ppb_input = {"layers_description": ppb_lay_desc} 

518 elif compute_ppb == 2: 

519 self._ppb_obj.ppb_combine_bodies_new(ppb_lay_desc) 

520 ppb_input = {"layers_description_new": ppb_lay_desc} 

521 if args[4]: # Plot 

522 self.__plot_stages(stages, stage_misc["stat"], stage_misc["dyn"]) 

523 return insights, ppb_input 

524 

525 def __update_evaluator(self, node, verbose): 

526 if self.evaluator_instances and node == self._ctx.head_node: 

527 tmp_eval = self.evaluator_instances.pop(0) 

528 self._ccfg = tmp_eval.ccfg 

529 self._ctx.copy_tmp_buff(tmp_eval.ctx) 

530 self._ctx = tmp_eval.ctx 

531 if verbose: 

532 logger.info( 

533 "Update ccfg and ctx, module : %s", 

534 self._ccfg.model_name, 

535 ) 

536 

537 def __update_next_layer_custom_function(self, *args): 

538 """Apply the next layer custom hook before evaluating a layer.""" 

539 flatten, verbose = args[0], args[1] 

540 record_lay_types = args[2] 

541 stage_id, chunk_id, lay_id = args[3], args[4], args[5] 

542 node = args[6] 

543 if self.is_regular_layer(node) and flatten: 

544 hook = flatten.pop(0) 

545 if hook: 

546 if verbose: 

547 logger.info("Apply hook %s", hook.__name__) 

548 record_lay_types[(stage_id, chunk_id, lay_id)] = ( 

549 self._ccfg, 

550 self._ctx, 

551 hook, 

552 ) 

553 hook(self) 

554 else: 

555 record_lay_types[(stage_id, chunk_id, lay_id)] = ( 

556 self._ccfg, 

557 self._ctx, 

558 lambda _: None, 

559 ) 

560 else: 

561 record_lay_types[(stage_id, chunk_id, lay_id)] = ( 

562 self._ccfg, 

563 self._ctx, 

564 lambda _: None, 

565 ) 

566 if verbose: 

567 logger.info( 

568 "stage_id=%s, chunk_id=%s, lay_id=%s, node=%s", 

569 stage_id, 

570 chunk_id, 

571 lay_id, 

572 node, 

573 ) 

574 self._ccfg.print_parallelism() 

575 

576 def __chunk_stage_lay_loops(self, *args): 

577 """Evaluate every layer in every stage and chunk.""" 

578 flatten, stages, record_lay_types = args[0], args[1], args[2] 

579 sm = args[3] 

580 verbose, compute_ppb, ppb_lay_desc = args[4], args[5], args[6] 

581 self._ctx.real_lay_ids = [] 

582 count = 0 

583 for chunk_id in range(self._ccfg.vp): 

584 self._ctx.real_lay_ids += [[]] 

585 for stage_id in range(self._ccfg.p): 

586 self._ctx.real_lay_ids[chunk_id] += [[]] 

587 self._ctx.init_tmp_buff() 

588 for lay_id in range(len(stages[stage_id][chunk_id])): 

589 node = stages[stage_id][chunk_id][lay_id] 

590 if self.is_regular_layer(node): 

591 self._ctx.real_lay_ids[chunk_id][stage_id] += [count] 

592 count += 1 

593 else: 

594 self._ctx.real_lay_ids[chunk_id][stage_id] += [""] 

595 # Update evaluator (multimodal) 

596 self.__update_evaluator(node, verbose) 

597 # Update next layer custom function 

598 self.__update_next_layer_custom_function( 

599 flatten, 

600 verbose, 

601 record_lay_types, 

602 stage_id, 

603 chunk_id, 

604 lay_id, 

605 node, 

606 ) 

607 self._ctx.current_stage_id = stage_id 

608 self._ctx.current_chunk_id = chunk_id 

609 self._ctx.current_lay_id = lay_id 

610 self._ctx.current_node = node 

611 static_mem = self._inner_static_mem() 

612 sm["stat"][stage_id][chunk_id][lay_id] = static_mem 

613 sm["dyn"][stage_id][chunk_id][lay_id] = sum( 

614 self._inner_dynamic_mem() 

615 ) 

616 if verbose: 

617 logger.info("pp micro factor for dynamic: %s",self._ctx.micro_factor) 

618 # PPB Purpose 

619 if compute_ppb == 1: 

620 desc = self._ppb_obj.lay_ppb( 

621 self._ccfg, 

622 self._ctx, 

623 sm["stat"][stage_id][chunk_id][lay_id], 

624 ) 

625 self._ppb_obj.add_to_ppb_list(ppb_lay_desc, desc) 

626 elif compute_ppb == 2: 

627 desc = self._ppb_obj.lay_ppb_new( 

628 self._ccfg, 

629 self._ctx, 

630 sm["stat"][stage_id][chunk_id][lay_id], 

631 ) 

632 self._ppb_obj.add_to_ppb_list(ppb_lay_desc, desc) 

633 self.__update_stage_logs(sm["logs"], stage_id) 

634 

635 def __postprocess_stages(self, *args): 

636 """Build memory insights from raw stage evaluation buffers.""" 

637 stages, record_lay_types = args[0], args[1] 

638 sm = args[2] 

639 verbose, spec_stage_id = args[3], args[4] 

640 insights = args[5] 

641 for stage_id in range(self._ccfg.p): 

642 ins = {} # Mem Insights purpose 

643 ins["Static"] = sum( 

644 sum(mem for mem in c) for c in sm["stat"][stage_id] 

645 ) 

646 ins["Dynamic"] = sum( 

647 sum(mem for mem in c) for c in sm["dyn"][stage_id] 

648 ) 

649 self._ctx.init_tmp_buff() 

650 if not self._ccfg.freeze: 

651 ins["Dynamic"] += self._overhead_obj.estimate( 

652 stages, stage_id, record_lay_types 

653 ) 

654 self.__update_stage_logs(sm["logs"], stage_id) 

655 safety_buffer = 1024 * 1024 * 1024 # 1 GB 

656 if ins["Dynamic"] > 0: 

657 ins["Dynamic"] += safety_buffer 

658 stage_accu = sm["logs"][stage_id].accu_mem_type 

659 ins["ModelParameters"] = self.mb(stage_accu[MemType.MODEL_PARAM]) 

660 ins["OptimizerStates"] = self.mb(stage_accu[MemType.OPTIM_STATE]) 

661 ins["AccumulGradients"] = self.mb(stage_accu[MemType.ACCU_GRAD]) 

662 ins["Attn"] = self.mb(stage_accu[MemType.ATTN_ACTIV]) 

663 ins["FFn"] = self.mb(stage_accu[MemType.FFN_ACTIV]) 

664 ins["Norm"] = self.mb(stage_accu[MemType.NORM_ACTIV]) 

665 ins["AllGather Comm"] = self.mb(stage_accu[MemType.AG_COMM]) 

666 ins["All2All Comm"] = self.mb(stage_accu[MemType.A2A_COMM]) 

667 ins["Node Log"] = sm["logs"][stage_id].node_compute_log 

668 # VERBOSE 

669 if verbose and spec_stage_id in (-1, stage_id): 

670 self.__verbose_insights(sm, stage_id, ins) 

671 ins["Static"] = self.mb(ins["Static"]) 

672 ins["Dynamic"] = self.mb(ins["Dynamic"]) 

673 insights += [ins] 

674 

675 def __verbose_insights(self, *args): 

676 """logging purpose""" 

677 sm = args[0] 

678 stage_id = args[1] 

679 ins = args[2] 

680 stat_i = max(1, ins["Static"]) 

681 dyn_i = max(1, ins["Dynamic"]) 

682 # logs_i = sm["logs"][stage_id] 

683 accu_i = sm["logs"][stage_id].accu_mem_type 

684 logger.info( 

685 "stage _%s : %s MB", 

686 stage_id, 

687 self.mb(ins["Static"] + ins["Dynamic"]), 

688 ) 

689 logger.info( 

690 "\tStatic\t%s\t" 

691 "ModelParam %s (%s%%), " 

692 "OptimStates %s (%s%%), " 

693 "Gradients %s (%s%%)", 

694 self.mb(ins["Static"]), 

695 ins["ModelParameters"], 

696 round(accu_i[MemType.MODEL_PARAM] / stat_i * 100), 

697 ins["OptimizerStates"], 

698 round(accu_i[MemType.OPTIM_STATE] / stat_i * 100), 

699 ins["AccumulGradients"], 

700 round(accu_i[MemType.ACCU_GRAD] / stat_i * 100), 

701 ) 

702 logger.info( 

703 "\tDynamic\t%s\t" 

704 "Attn %s (%d%%), " 

705 "FFn %s (%d%%), " 

706 "Norm %s (%d%%), " 

707 "AllGather Comm %s (%d%%), " 

708 "All2All Comm %s (%d%%), ", 

709 self.mb(ins["Dynamic"]), 

710 ins["Attn"], 

711 round(accu_i[MemType.ATTN_ACTIV] / dyn_i * 100), 

712 ins["FFn"], 

713 round(accu_i[MemType.FFN_ACTIV] / dyn_i * 100), 

714 ins["Norm"], 

715 round(accu_i[MemType.NORM_ACTIV] / dyn_i * 100), 

716 ins["AllGather Comm"], 

717 round(accu_i[MemType.AG_COMM] / dyn_i * 100), 

718 ins["All2All Comm"], 

719 round(accu_i[MemType.A2A_COMM] / dyn_i * 100), 

720 ) 

721 logger.info( 

722 "\tNode eval log : \n %s \n %s", 

723 "> Foreach: (stage_id,chunk_id,lay_id,name) -> (mem type,value)", 

724 pprint.pformat(ins["Node Log"], width=300), 

725 ) 

726 

727 def apply_hook(self, hook, ccfg=None, ctx=None): 

728 """apply hook on evaluator""" 

729 self._ccfg = ccfg if ccfg else self._ccfg 

730 self._ctx = ctx if ctx else self._ctx 

731 hook(self) 

732 

733 def set_layer_custom(self, _): 

734 """child implement""" 

735 pass # pylint: disable=unnecessary-pass 

736 

737 def is_regular_layer(self, _): 

738 """child implement""" 

739 return False 

740 

741 def import_eval_yaml(self): 

742 """child implement""" 

743 pass # pylint: disable=unnecessary-pass 

744 

745 def get_num_layers(self): 

746 """child implement""" 

747 return 0 

748 

749 def get_strategy(self): 

750 """child implement""" 

751 return {} 

752 

753 def get_max_device_memory(self): 

754 """child implement""" 

755 return 0 

756 

757 def print_stages(self, _): 

758 """child implement""" 

759 pass # pylint: disable=unnecessary-pass 

760 

761 def print_ctx(self): 

762 """child implement""" 

763 pass # pylint: disable=unnecessary-pass