You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

pass.cc 19 kB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /**
  2. * Copyright 2019 Huawei Technologies Co., Ltd
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "pipeline/jit/pass.h"
  17. #include <memory>
  18. #include <vector>
  19. #include <string>
  20. #include <unordered_map>
  21. #include <algorithm>
  22. #include "ir/func_graph_cloner.h"
  23. #include "pipeline/jit/parse/parse_base.h"
  24. #include "pipeline/jit/resource.h"
  25. #include "pipeline/jit/validator.h"
  26. #include "pipeline/jit/remove_value_node_dup.h"
  27. #include "frontend/optimizer/optimizer.h"
  28. #include "frontend/optimizer/cse_pass.h"
  29. #include "frontend/optimizer/graph_kernel_reuse.h"
  30. #include "frontend/optimizer/clean.h"
  31. #include "frontend/optimizer/irpass.h"
  32. #include "frontend/optimizer/control_depend.h"
  33. #include "frontend/optimizer/graph_transform.h"
  34. #include "frontend/parallel/step_parallel.h"
  35. #include "frontend/parallel/step_auto_parallel.h"
  36. #include "frontend/parallel/allreduce_fusion/step_allreduce_fusion.h"
  37. #include "utils/log_adapter.h"
  38. namespace mindspore {
  39. namespace pipeline {
  40. using OptPassGroupMap = opt::OptPassGroupMap;
  41. using Optimizer = opt::Optimizer;
  42. using CompileGraphs = compile::CompileGraphs;
  43. using abstract::AnalysisResult;
  44. using mindspore::abstract::AnalysisContextPtr;
  45. using mindspore::validator::Validate;
  46. bool SimplifyDataStructuresPass(const ResourcePtr &res) {
  47. MS_EXCEPTION_IF_NULL(res->func_graph());
  48. FuncGraphPtr func_graph = res->func_graph();
  49. bool changed = opt::SimplifyDataStructures(func_graph, res->manager());
  50. abstract::AbstractBasePtrList args_spec;
  51. auto parameters = func_graph->parameters();
  52. (void)std::transform(parameters.begin(), parameters.end(), std::back_inserter(args_spec),
  53. [](const AnfNodePtr &p) -> AbstractBasePtr { return p->abstract(); });
  54. if (changed) {
  55. FuncGraphPtr new_fg = Renormalize(res, func_graph, args_spec);
  56. res->set_func_graph(new_fg);
  57. }
  58. res->set_args_spec(args_spec);
  59. return true;
  60. }
  61. bool CleanAfterOptAPass(const ResourcePtr &res) {
  62. MS_EXCEPTION_IF_NULL(res->func_graph());
  63. FuncGraphPtr func_graph = res->func_graph();
  64. bool changed = opt::CleanAfterOptA(func_graph, res->manager());
  65. abstract::AbstractBasePtrList args_spec;
  66. auto parameters = func_graph->parameters();
  67. (void)std::transform(parameters.begin(), parameters.end(), std::back_inserter(args_spec),
  68. [](const AnfNodePtr &p) -> AbstractBasePtr { return p->abstract(); });
  69. if (changed) {
  70. FuncGraphPtr new_fg = Renormalize(res, func_graph, args_spec);
  71. res->set_func_graph(new_fg);
  72. }
  73. res->set_args_spec(args_spec);
  74. return true;
  75. }
  76. namespace {
  77. OptPassGroupMap GetOptPassesA(const opt::irpass::OptimizeIRPassLib &irpass) {
  78. opt::OptPassConfig a_1 = opt::OptPassConfig({
  79. irpass.switch_layer_defer_inline_,
  80. irpass.switch_simplify_,
  81. // Safe inlining
  82. irpass.inline_,
  83. irpass.partial_eliminate_,
  84. irpass.replace_applicator_,
  85. // Specialization
  86. irpass.specialize_transform_,
  87. // Miscellaneous
  88. irpass.item_tuple_eliminate_,
  89. irpass.env_get_item_eliminate_,
  90. irpass.cast_eliminate_,
  91. irpass.reshape_eliminate_,
  92. irpass.reduce_eliminate_,
  93. irpass.tile_eliminate_,
  94. irpass.transpose_eliminate_,
  95. irpass.minmaximum_grad_,
  96. irpass.get_make_ref_eliminate_,
  97. // Arithmetic simplifications
  98. irpass.arithmetic_simplify_,
  99. irpass.addn_zero_filter_,
  100. irpass.adjust_all_reduce_mul_add_,
  101. irpass.accumulaten_eliminater_,
  102. // Safe inlining
  103. irpass.inline_,
  104. irpass.sparse_tensor_eliminate_,
  105. });
  106. opt::OptPassConfig a_2 = opt::OptPassConfig({
  107. irpass.merge_addn_,
  108. irpass.float_tuple_getitem_switch_,
  109. irpass.float_env_getitem_switch_,
  110. irpass.incorporate_getitem_set_,
  111. irpass.incorporate_call_,
  112. irpass.incorporate_call_switch_,
  113. irpass.incorporate_env_getitem_bypass_recursive_,
  114. irpass.incorporate_env_getitem_switch_,
  115. irpass.new_env_get_item_,
  116. irpass.depend_value_elim_,
  117. irpass.all_reduce_const_elim_,
  118. });
  119. opt::OptPassConfig a_after_grad = opt::OptPassConfig({
  120. irpass.inline_without_move_,
  121. });
  122. opt::OptPassConfig a_3 = opt::OptPassConfig({
  123. irpass.arithmetic_simplify2_,
  124. irpass.same_eliminate_,
  125. irpass.check_bprop_eliminate_,
  126. irpass.switch_layer_defer_inline_,
  127. irpass.replace_applicator_,
  128. });
  129. opt::OptPassConfig virtual_dataset = opt::OptPassConfig({irpass.virtual_dataset_eliminate_});
  130. opt::OptPassConfig grad = opt::OptPassConfig({irpass.expand_jprim_}, true);
  131. opt::irpass::ResolveIRPassLib resolve_irpass;
  132. opt::OptPassConfig resolve_pass =
  133. opt::OptPassConfig({resolve_irpass.resolver_resolve_, resolve_irpass.resolver_getattr_,
  134. irpass.get_make_ref_eliminate_, irpass.replace_old_param_});
  135. OptPassGroupMap map_a({{"a_1", a_1},
  136. {"a_2", a_2},
  137. {"auto_parallel", opt::OptPassConfig(parallel::StepAutoParallel)},
  138. {"parallel", opt::OptPassConfig(parallel::StepParallel)},
  139. {"allreduce_fusion", opt::OptPassConfig(parallel::StepAllreduceFusion)},
  140. {"virtual_dataset", virtual_dataset},
  141. {"grad", grad},
  142. {"resolve", resolve_pass},
  143. {"a_after_grad", a_after_grad},
  144. {"renormalize", opt::OptPassConfig::Renormalize()},
  145. {"cse", opt::OptPassConfig(opt::CSEPass(false))},
  146. {"a_3", a_3}});
  147. return map_a;
  148. }
  149. OptPassGroupMap GetA1A2(const opt::irpass::OptimizeIRPassLib &irpass) {
  150. auto opt_a = GetOptPassesA(irpass);
  151. OptPassGroupMap a1_a2({opt_a[0], opt_a[1]});
  152. return a1_a2;
  153. }
  154. OptPassGroupMap GetOptPassesAfterCconv(const opt::irpass::OptimizeIRPassLib &irpass) {
  155. opt::OptPassConfig c_1 = opt::OptPassConfig({
  156. // Safe inlining,
  157. irpass.inline_,
  158. irpass.partial_eliminate_,
  159. });
  160. OptPassGroupMap map_a({{"c_1", c_1},
  161. {"cse", opt::OptPassConfig(opt::CSEPass(false))},
  162. {"renormalize", opt::OptPassConfig::Renormalize()}});
  163. return map_a;
  164. }
  165. OptPassGroupMap GetOptPassesTransformGraph(const opt::irpass::OptimizeIRPassLib &irpass) {
  166. opt::OptPassConfig d_1 = opt::OptPassConfig({// Safe inlining
  167. irpass.call_graph_tuple_transform_, irpass.item_tuple_eliminate_});
  168. OptPassGroupMap map_a({{"d_1", d_1}, {"renormalize", opt::OptPassConfig::Renormalize()}});
  169. return map_a;
  170. }
  171. OptPassGroupMap GetOptPassesB(const opt::irpass::OptimizeIRPassLib &irpass) {
  172. opt::OptPassConfig b_1 = opt::OptPassConfig(
  173. {irpass.zero_like_fill_zero_, irpass.item_tuple_eliminate_, irpass.float_tuple_getitem_switch_,
  174. irpass.reset_defer_inline_, irpass.inline_, irpass.special_op_eliminate_, irpass.get_make_ref_eliminate_,
  175. irpass.incorporate_env_getitem_, irpass.incorporate_env_getitem_switch_, irpass.env_get_item_eliminate_,
  176. irpass.incorporate_env_getitem_switch_layer_, irpass.value_based_eliminate_});
  177. opt::OptPassConfig b_2 = opt::OptPassConfig({
  178. irpass.replace_refkey_by_param_,
  179. irpass.make_ref_eliminate_,
  180. irpass.get_ref_param_eliminate_,
  181. irpass.row_tensor_eliminate_,
  182. });
  183. OptPassGroupMap map({
  184. {"b_1", b_1},
  185. {"b_2", b_2},
  186. {"renormalize", opt::OptPassConfig::Renormalize()},
  187. {"cse", opt::OptPassConfig(opt::CSEPass(false))},
  188. });
  189. return map;
  190. }
  191. OptPassGroupMap GetOptPassesPynativeElim(const opt::irpass::OptimizeIRPassLib &irpass) {
  192. opt::OptPassConfig pynative_eliminate = opt::OptPassConfig({
  193. irpass.pynative_eliminate_,
  194. });
  195. OptPassGroupMap map({
  196. {"pynative_eliminate", pynative_eliminate},
  197. });
  198. return map;
  199. }
  200. OptPassGroupMap GetOptPassesGraphKernelA(const opt::irpass::OptimizeIRPassLib &irpass) {
  201. opt::OptPassConfig interface_fusion = opt::OptPassConfig({
  202. irpass.mark_interface_fusion_,
  203. });
  204. OptPassGroupMap map({
  205. {"graph_kernel_reuse", opt::OptPassConfig(opt::GraphKernelReuse())},
  206. {"interface_fusion", interface_fusion},
  207. {"renormalize", opt::OptPassConfig::Renormalize()},
  208. {"cse", opt::OptPassConfig(opt::CSEPass(false))},
  209. });
  210. return map;
  211. }
  212. OptPassGroupMap GetOptPassesGraphKernelB(const opt::irpass::OptimizeIRPassLib &irpass) {
  213. opt::OptPassConfig elim_1 = opt::OptPassConfig({
  214. irpass.addn_eliminate_,
  215. irpass.incorporate_getitem_from_param_,
  216. });
  217. opt::OptPassConfig elim_2 = opt::OptPassConfig({
  218. irpass.unused_parameter_eliminate_,
  219. irpass.unused_output_eliminate_,
  220. });
  221. OptPassGroupMap map({
  222. {"elim_1", elim_1},
  223. {"renormalize", opt::OptPassConfig::Renormalize()},
  224. {"elim_2", elim_2},
  225. });
  226. return map;
  227. }
  228. OptPassGroupMap GetOptPassesC(const opt::irpass::OptimizeIRPassLib &irpass) {
  229. return OptPassGroupMap({{"renormalize", opt::OptPassConfig::Renormalize()}});
  230. }
  231. OptPassGroupMap GetControlPhases(const opt::irpass::OptimizeIRPassLib &irpass) {
  232. opt::OptPassConfig control_group = opt::OptPassConfig({irpass.convert_switch_replacement_}, true);
  233. OptPassGroupMap map({
  234. {"control_group", control_group},
  235. {"renormalize", opt::OptPassConfig::Renormalize()},
  236. });
  237. return map;
  238. }
  239. OptPassGroupMap GetInferenceOptPreparePhases() {
  240. opt::irpass::InferenceOptPrepareLib irpass;
  241. auto grad_var_prepare = opt::OptPassConfig({irpass.grad_var_prepare_});
  242. opt::OptPassGroupMap prepare_map({{"inference_opt_prep", grad_var_prepare}});
  243. return prepare_map;
  244. }
  245. OptPassGroupMap GetPreparePhases(const opt::irpass::OptimizeIRPassLib &irpass) {
  246. opt::OptPassConfig prepare_group = opt::OptPassConfig({irpass.print_tuple_wrapper_});
  247. OptPassGroupMap map({{"prepare_group", prepare_group}});
  248. return map;
  249. }
  250. static std::unordered_map<std::string, std::shared_ptr<Optimizer>> g_pass_opts = {};
  251. void InitOpt(const ResourcePtr &res) {
  252. if (g_pass_opts.size() == 0) {
  253. opt::irpass::OptimizeIRPassLib irpass;
  254. g_pass_opts["a1a2"] = Optimizer::MakeOptimizer("a1a2", res, GetA1A2(irpass));
  255. g_pass_opts["opt_a"] = Optimizer::MakeOptimizer("opt_a", res, GetOptPassesA(irpass));
  256. g_pass_opts["opt_b"] = Optimizer::MakeOptimizer("opt_b", res, GetOptPassesB(irpass), false, true);
  257. g_pass_opts["opt_after_cconv"] =
  258. Optimizer::MakeOptimizer("opt_after_cconv", res, GetOptPassesAfterCconv(irpass), false, true);
  259. g_pass_opts["opt_trans_graph"] =
  260. Optimizer::MakeOptimizer("opt_trans_graph", res, GetOptPassesTransformGraph(irpass), true, true);
  261. g_pass_opts["opt_graph_kernel_a"] =
  262. Optimizer::MakeOptimizer("opt_graph_kernel_a", res, GetOptPassesGraphKernelA(irpass), true);
  263. g_pass_opts["opt_graph_kernel_b"] =
  264. Optimizer::MakeOptimizer("opt_graph_kernel_b", res, GetOptPassesGraphKernelB(irpass), false);
  265. g_pass_opts["renormal"] = Optimizer::MakeOptimizer("renormal", res, GetOptPassesC(irpass));
  266. g_pass_opts["opt_control"] = Optimizer::MakeOptimizer("opt_control", res, GetControlPhases(irpass), false, true);
  267. g_pass_opts["opt_prepare"] = Optimizer::MakeOptimizer("opt_prepare", res, GetPreparePhases(irpass));
  268. auto context_ptr = MsContext::GetInstance();
  269. MS_EXCEPTION_IF_NULL(context_ptr);
  270. if (!(context_ptr->get_param<bool>(MS_CTX_ENABLE_GRAPH_KERNEL))) {
  271. g_pass_opts["opt_graph_kernel_a"]->set_enable(false);
  272. g_pass_opts["opt_graph_kernel_b"]->set_enable(false);
  273. }
  274. }
  275. }
  276. } // namespace
  277. void ReclaimOptimizer() {
  278. for (auto &opt : g_pass_opts) {
  279. opt.second = nullptr;
  280. }
  281. g_pass_opts.clear();
  282. }
  283. bool OptPassGroup(const ResourcePtr &res, const std::string &name) {
  284. if (res->func_graph() == nullptr) {
  285. MS_LOG(ERROR) << "Opt passes int error";
  286. return false;
  287. }
  288. FuncGraphPtr func_graph = res->func_graph();
  289. MS_LOG(DEBUG) << "Start " << name << " func graph:" << func_graph->ToString() << ", "
  290. << func_graph->get_return()->DebugString(true);
  291. InitOpt(res);
  292. if (g_pass_opts.find(name) != g_pass_opts.end()) {
  293. res->set_func_graph(g_pass_opts[name]->step(func_graph));
  294. }
  295. // Note: StepParallel may modify the AbstractValue of the parameters of func_graph, but they are not updated to
  296. // res->args_spec_ yet. So if any later pass or action want to use that variable, it should be set here.
  297. return true;
  298. }
  299. bool OptPassA1A2(const ResourcePtr &res) { return OptPassGroup(res, "a1a2"); }
  300. bool OptPassAGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_a"); }
  301. bool OptPassBGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_b"); }
  302. bool OptPassAfterCconvGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_after_cconv"); }
  303. bool OptPassTransformGraphGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_trans_graph"); }
  304. bool OptPassGraphKernelGroupA(const ResourcePtr &res) { return OptPassGroup(res, "opt_graph_kernel_a"); }
  305. bool OptPassGraphKernelGroupB(const ResourcePtr &res) { return OptPassGroup(res, "opt_graph_kernel_b"); }
  306. bool ControlGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_control"); }
  307. bool PrepareGroup(const ResourcePtr &res) { return OptPassGroup(res, "opt_prepare"); }
  308. bool OptPassRNGroup(const ResourcePtr &res) { return OptPassGroup(res, "renormal"); }
  309. bool AddControlDependPass(const ResourcePtr &res) {
  310. FuncGraphPtr func_graph = res->func_graph();
  311. MS_EXCEPTION_IF_NULL(func_graph);
  312. if (func_graph->has_flag(GRAPH_FLAG_EFFECT_PATIAL_ORDER)) {
  313. opt::AddControlDepend(func_graph);
  314. }
  315. for (auto fg : func_graph->func_graphs_used_total()) {
  316. MS_EXCEPTION_IF_NULL(fg);
  317. if (fg->has_flag(GRAPH_FLAG_EFFECT_PATIAL_ORDER)) {
  318. opt::AddControlDepend(fg);
  319. }
  320. }
  321. return true;
  322. }
  323. bool MergeDupGraphPass(const ResourcePtr &res) {
  324. FuncGraphPtr func_graph = res->func_graph();
  325. MS_EXCEPTION_IF_NULL(func_graph);
  326. MS_EXCEPTION_IF_NULL(res->manager());
  327. if (res->manager()->func_graphs().size() <= 1) {
  328. return true;
  329. }
  330. return MergeDuplicateGraphs(res->manager());
  331. }
  332. bool RemoveValueNodeDuplicationsPass(const ResourcePtr &res) {
  333. if (res->func_graph() == nullptr) {
  334. MS_LOG(EXCEPTION) << "Remove value node duplications error.";
  335. }
  336. auto manager = res->manager();
  337. HashCache hash_cache;
  338. HashValue hashes;
  339. // Remove duplicated value nodes across all graphs in manager
  340. for (auto &fg : manager->func_graphs()) {
  341. auto value_nodes = fg->value_nodes();
  342. for (const auto &value_pair : value_nodes) {
  343. TryToDoReplace(manager.get(), value_pair.first, &hash_cache, &hashes);
  344. }
  345. }
  346. return true;
  347. }
  348. bool CconvPass(const ResourcePtr &res) {
  349. MS_EXCEPTION_IF_NULL(res->func_graph());
  350. FuncGraphPtr func_graph = res->func_graph();
  351. FuncGraphPtr new_fg = LiftingClone(func_graph);
  352. res->set_func_graph(new_fg);
  353. return true;
  354. }
  355. bool TransformTopGraphPass(const ResourcePtr &res) {
  356. if (res->func_graph() == nullptr) {
  357. MS_LOG(EXCEPTION) << "Transform top graph error.";
  358. }
  359. FuncGraphPtr func_graph = res->func_graph();
  360. if (opt::FuncGraphHasTupleInput(func_graph)) {
  361. opt::GraphTupleParamTransform graph_trans;
  362. func_graph = graph_trans(func_graph, res->manager());
  363. res->set_func_graph(func_graph);
  364. AbstractBasePtrList abs_spec_list;
  365. auto &params = func_graph->parameters();
  366. std::transform(params.begin(), params.end(), std::back_inserter(abs_spec_list),
  367. [](AnfNodePtr node) { return node->abstract(); });
  368. res->set_args_spec(abs_spec_list);
  369. }
  370. return true;
  371. }
  372. bool ValidatePass(const ResourcePtr &res) {
  373. MS_EXCEPTION_IF_NULL(res->func_graph());
  374. FuncGraphPtr func_graph = res->func_graph();
  375. Validate(func_graph);
  376. return true;
  377. }
  378. bool InferenceOptPreparePass(const ResourcePtr &res) {
  379. FuncGraphPtr func_graph = res->func_graph();
  380. MS_EXCEPTION_IF_NULL(func_graph);
  381. auto prepare_map = GetInferenceOptPreparePhases();
  382. auto infer_opt_prepare = opt::Optimizer::MakeOptimizer("inference_prepare", res, prepare_map);
  383. (void)infer_opt_prepare->step(func_graph, false);
  384. return true;
  385. }
  386. bool PynativeOptPass(const ResourcePtr &res) {
  387. FuncGraphPtr func_graph = res->func_graph();
  388. MS_EXCEPTION_IF_NULL(func_graph);
  389. opt::irpass::OptimizeIRPassLib irpass;
  390. auto pynative_opt = GetOptPassesPynativeElim(irpass);
  391. auto pynative_opt_opt = opt::Optimizer::MakeOptimizer("pynative_opt", res, pynative_opt);
  392. (void)pynative_opt_opt->step(func_graph, false);
  393. return true;
  394. }
  395. std::vector<PassItem> kVmPasses = {{"simplify_data_structures", SimplifyDataStructuresPass},
  396. {"opt_a", OptPassAGroup},
  397. {"clean_after_opta", CleanAfterOptAPass},
  398. {"opt_b", OptPassBGroup},
  399. {"cconv", CconvPass},
  400. {"opt_after_cconv", OptPassAfterCconvGroup},
  401. {"remove_dup_value", RemoveValueNodeDuplicationsPass},
  402. {"tuple_transform", OptPassTransformGraphGroup},
  403. {"opt_graph_kernel_a", OptPassGraphKernelGroupA},
  404. {"opt_graph_kernel_b", OptPassGraphKernelGroupB},
  405. {"add_control_depend", AddControlDependPass}};
  406. std::vector<PassItem> kGePasses = {{"simplify_data_structures", SimplifyDataStructuresPass},
  407. {"opt_a", OptPassAGroup},
  408. {"clean_after_opta", CleanAfterOptAPass},
  409. {"opt_b", OptPassBGroup},
  410. {"add_control_depend", AddControlDependPass},
  411. {"opt_control", ControlGroup},
  412. {"opt_prepare", PrepareGroup},
  413. {"cconv", CconvPass}};
  414. std::vector<PassItem> kPynativePasses = {{"opt_a", OptPassAGroup},
  415. {"opt_b", OptPassBGroup},
  416. {"cconv", CconvPass},
  417. {"transform_top", TransformTopGraphPass},
  418. {"transform_graph", OptPassTransformGraphGroup}};
  419. std::vector<PassItem> kInlinePasses = {{"simplify_data_structures", SimplifyDataStructuresPass}, {"a1a2", OptPassA1A2}};
  420. } // namespace pipeline
  421. } // namespace mindspore