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

84 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-08-04 05:18 +0800

1# Copyright 2024-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"""run parallelization""" 

16 

17import argparse 

18import os 

19import sys 

20 

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

22from hyper_parallel.auto_parallel.sapp_nd.nd.logger import logger, set_verbose_level 

23import hyper_parallel.auto_parallel.sapp_nd.nd.parallelize as Par 

24import hyper_parallel.auto_parallel.sapp_nd.nd.dimensions as Dim 

25import hyper_parallel.auto_parallel.sapp_nd.nd.common.hardware as Hard 

26 

27 

28def _run_hyper_v2_search(cli_parser, cli_args): 

29 """Run the HyperParallel V2 strategy search via ``config_adapter``. 

30 

31 This branch is activated when ``-f hyper_v2`` is combined with 

32 ``-s/--search-config``. It reads the Search Config YAML, validates 

33 it, runs the ND search engine through :func:`search_strategies`, 

34 and writes the resolved strategy back into a copy of the original 

35 ``train.yaml``. 

36 

37 Args: 

38 cli_parser: The :class:`argparse.ArgumentParser` (used for ``error()``). 

39 cli_args: The parsed CLI namespace. Requires ``yaml_config``, 

40 ``search_config``, and optionally ``output_dir``. 

41 

42 Raises: 

43 SystemExit: If validation fails (via ``parser.error``). 

44 """ 

45 # pylint: disable=import-outside-toplevel 

46 from hyper_parallel.auto_parallel.config_adapter import ( 

47 read_search_config, 

48 validate, 

49 search_strategies, 

50 write_resolved_yaml, 

51 ) 

52 

53 if not os.path.isfile(cli_args.search_config): 

54 cli_parser.error(f"search-config not found: {cli_args.search_config}") 

55 if not os.path.isfile(cli_args.yaml_config): 

56 cli_parser.error(f"yaml-config not found: {cli_args.yaml_config}") 

57 

58 set_verbose_level(cli_args.verbosity) 

59 

60 search_cfg = read_search_config(cli_args.search_config) 

61 

62 if cli_args.global_batch_size is not None: 

63 search_cfg.constraint["global_batch_size"] = cli_args.global_batch_size 

64 if cli_args.devices is not None: 

65 cards_per_node = max(1, search_cfg.cluster_spec.get("cards_per_node", 8)) 

66 search_cfg.cluster_spec["num_nodes"] \ 

67 = max(1, cli_args.devices // cards_per_node) 

68 

69 errors = validate(search_cfg) 

70 hard_errors = [e for e in errors if e.severity == "error"] 

71 warnings = [e for e in errors if e.severity == "warning"] 

72 for w in warnings: 

73 logger.warning("%s: %s", w.field_path, w.message) 

74 if hard_errors: 

75 for e in hard_errors: 

76 logger.error("%s: %s", e.field_path, e.message) 

77 cli_parser.error( 

78 f"Search config validation failed with {len(hard_errors)} error(s)." 

79 ) 

80 

81 result = search_strategies(search_cfg) 

82 search_cfg.resolved_strategy = result 

83 

84 output_dir = cli_args.output_dir or "." 

85 if not os.path.isdir(output_dir): 

86 os.makedirs(output_dir, exist_ok=True) 

87 resolve_path = os.path.join(output_dir, "resolved.yaml") 

88 write_resolved_yaml(search_cfg, cli_args.yaml_config, resolve_path) 

89 logger.output("Resolved strategy written to %s", resolve_path) 

90 logger.output( 

91 "Optimal strategy: dp=%(dp)s tp=%(tp)s pp=%(pp)s " 

92 "cp=%(cp)s ep=%(ep)s mb_num=%(micro_batch_num)s " 

93 "mem=%(memory_estimate_mb).0f MB score=%(score).2e", 

94 result, 

95 ) 

96 

97 

98if __name__ == "__main__": 

99 parser = argparse.ArgumentParser( 

100 prog="python run_nd.py", 

101 description=("Provides a degree to *N* parallelism dimensions"), 

102 epilog="", 

103 ) 

104 

105 parser.add_argument( 

106 "-y", 

107 "--yaml_config", 

108 type=str, 

109 required=True, 

110 help="Path to yaml configuration file", 

111 ) 

112 parser.add_argument( 

113 "-f", 

114 "--framework", 

115 default="mindformers", 

116 type=str, 

117 required=False, 

118 help="Framework to evaluate in " 

119 "[mindformers, mindspeed, hyperparallel, hyper_v2, torchtitan]", 

120 ) 

121 parser.add_argument( 

122 "-d", 

123 "--devices", 

124 type=int, 

125 default=None, 

126 help="Number of devices. Takes yaml value if unspecified", 

127 ) 

128 parser.add_argument( 

129 "-b", 

130 "--global_batch_size", 

131 type=int, 

132 default=None, 

133 help="Global batch size. Takes yaml value if unspecified", 

134 ) 

135 parser.add_argument( 

136 "-m", 

137 "--model", 

138 type=str, 

139 default=None, 

140 help="Model Name to use. Takes yaml value if unspecified", 

141 ) 

142 # parser.add_argument( 

143 # "-g", 

144 # "--generate_yaml_in", 

145 # type=str, 

146 # default=None, 

147 # help="Generate all fitting yaml configurations in the given folder", 

148 # ) 

149 # parser.add_argument( 

150 # "-c", 

151 # "--csv", 

152 # type=str, 

153 # default=None, 

154 # help="Computes correlation coefficient from csv results file", 

155 # ) 

156 parser.add_argument( 

157 "-l", 

158 "--dimensions", 

159 nargs="*", 

160 type=str, 

161 default=None, 

162 help="list of varying (output) dimensions", 

163 ) 

164 # parser.add_argument( 

165 # "-j", 

166 # "--threads_num", 

167 # type=int, 

168 # default=None, 

169 # help="Number of threads for the space generation", 

170 # ) 

171 parser.add_argument( 

172 "-v", 

173 "--verbosity", 

174 type=int, 

175 default=2, 

176 help="Level of verbosity in range [0,6], " 

177 "0 being no output and 6 being debug level output. " 

178 "Plot and debug csv are generated from 2", 

179 ) 

180 # parser.add_argument( 

181 # "-k", 

182 # "--ppb_k", 

183 # type=int, 

184 # default=None, 

185 # help="choose configuration number k for ppb", 

186 # ) 

187 parser.add_argument( 

188 "-A", 

189 "--device_type", 

190 default="A2", 

191 help="choose device type between A2 or A3", 

192 ) 

193 parser.add_argument( 

194 "-swap_os", 

195 "--swap_opt_state", 

196 action=argparse.BooleanOptionalAction, 

197 default=False, 

198 help="Activate swap optimiezr state", 

199 ) 

200 # parser.add_argument( 

201 # "-lm", 

202 # "--less_memory", 

203 # action=argparse.BooleanOptionalAction, 

204 # default=False, 

205 # help="Activate less memory schedule", 

206 # ) 

207 parser.add_argument( 

208 "-mppb", 

209 "-–manual_pipeline_balance", 

210 action=argparse.BooleanOptionalAction, 

211 default=False, 

212 help="Takes offset and recompute from yaml", 

213 ) 

214 parser.add_argument( 

215 "-t", 

216 "--top_config_number", 

217 type=int, 

218 default=None, 

219 help="Number of top configs to print & plot", 

220 ) 

221 parser.add_argument( 

222 "-mem", 

223 "--mem_for_ppb", 

224 type=str, 

225 default="0GB", 

226 help="Memory to reserve for pipeline balancing. " 

227 "Will be decreased from the memory budget allowed by ND (default 0GB)", 

228 ) 

229 parser.add_argument( 

230 "-c", 

231 "--cache_file", 

232 type=str, 

233 default=None, 

234 help="Cache file with ratios to recalibrate ND scores. " 

235 "Will be defaulted to 'None'.", 

236 ) 

237 

238 parser.add_argument( 

239 "-M", 

240 "--max_mem", 

241 type=str, 

242 default=None, 

243 help="Memory to reserve for pipeline balancing. " 

244 "Will be decreased from the memory budget allowed by ND (default 0GB)", 

245 ) 

246 parser.add_argument( 

247 "--train-yaml", 

248 type=str, 

249 default=None, 

250 help="Path to training configuration yaml file (for hyperparallel2)", 

251 ) 

252 parser.add_argument( 

253 "--accelerate-yaml", 

254 type=str, 

255 default=None, 

256 help="Path to accelerate configuration yaml file (for hyperparallel2)", 

257 ) 

258 parser.add_argument( 

259 "-s", 

260 "--search-config", 

261 type=str, 

262 default=None, 

263 help="Path to Search Config YAML for fine-grained search-space control " 

264 "(hyper_v2 only). Scalar=fixed, list=candidates, 'auto'=ND decides.", 

265 ) 

266 parser.add_argument( 

267 "-o", 

268 "--output-dir", 

269 type=str, 

270 default=None, 

271 help="Directory for output files when using --search-config " 

272 "(default: current directory).", 

273 ) 

274 

275 args = parser.parse_args() 

276 

277 max_mem = ( 

278 Memory.from_string(args.max_mem.strip()) 

279 if args.max_mem is not None 

280 else None 

281 ) 

282 

283 if args.cache_file is not None: 

284 if not os.path.exists(args.cache_file): 

285 logger.error( 

286 f"cache file not found:" 

287 f" {args.cache_file}" 

288 "\nProceeding without cache file..." 

289 ) 

290 args.cache_file = None 

291 

292 if args.framework == "hyper_v2" and args.search_config: 

293 _run_hyper_v2_search(parser, args) 

294 sys.exit(0) 

295 

296 set_verbose_level(args.verbosity) 

297 dims = Dim.get_dims(args.dimensions) 

298 YAML_FOLDER = None # args.generate_yaml_in 

299 machine = Hard.Machine(args.devices, args.device_type) 

300 

301 if args.framework == "hyperparallel2": 

302 if args.yaml_config is None or args.train_yaml is None or args.accelerate_yaml is None: 

303 parser.error("-y (model yaml), --train-yaml, and --accelerate-yaml are required for hyperparallel2") 

304 input_config = { 

305 "model": args.yaml_config, 

306 "train": args.train_yaml, 

307 "accelerate": args.accelerate_yaml, 

308 "machine": args.devices 

309 } 

310 elif args.framework == "torchtitan": 

311 module, config = args.yaml_config.split(":") 

312 input_config = { 

313 "module": module, 

314 "config": config, 

315 "machine": machine, 

316 } 

317 else: 

318 input_config = args.yaml_config 

319 

320 nd_runner = Par.Parallelize( 

321 args.framework, 

322 input_config, 

323 machine, 

324 global_batch_size=args.global_batch_size, 

325 dimensions=dims, 

326 swap_os=args.swap_opt_state, 

327 mppb=args.mppb, 

328 model=args.model, 

329 # model="Telecom", # args.model ====ONLY FOR XINYU BRANCH==== 

330 max_mem=max_mem, 

331 mem_for_ppb=Memory.from_string(args.mem_for_ppb.strip()), 

332 # vpp_less_mem=args.less_memory, 

333 ) 

334 

335 if YAML_FOLDER and not os.path.exists(YAML_FOLDER): 

336 os.makedirs(YAML_FOLDER) 

337 

338 space = nd_runner.run_generation_to_ordering( 

339 YAML_FOLDER, 

340 threads_num=None, # args.threads_num 

341 top_num=args.top_config_number, 

342 cache_file=args.cache_file, 

343 )