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.

tbe_kernel_build.cc 39 kB

6 years ago
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  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 "kernel/tbe/tbe_kernel_build.h"
  17. #include <memory>
  18. #include <map>
  19. #include <algorithm>
  20. #include <unordered_set>
  21. #include "operator/ops.h"
  22. #include "session/anf_runtime_algorithm.h"
  23. #include "kernel/tbe/tbe_kernel_mod.h"
  24. #include "kernel/tbe/tbe_adapter.h"
  25. #include "kernel/tbe/tbe_python_funcs.h"
  26. #include "kernel/tbe/tbe_convert_utils.h"
  27. #include "kernel/tbe/tbe_utils.h"
  28. namespace mindspore {
  29. namespace kernel {
  30. using mindspore::kernel::tbe::TbeAdapter;
  31. using mindspore::kernel::tbe::TbeUtils;
  32. constexpr auto kFusionOpList = "op_list";
  33. constexpr auto kFusionKernelNamePrfix = "te_fusion";
  34. constexpr auto kOptional = "optional_";
  35. constexpr auto kOpFormat_FRACTAL_Z = "FRACTAL_Z";
  36. std::string NormalizeFullScopeName(const string &full_scope_name) {
  37. // exp:Default/ReLU-op0 -->Default_ReLU_op0
  38. string normal_ret = full_scope_name;
  39. std::replace(normal_ret.begin(), normal_ret.end(), '/', '_');
  40. std::replace(normal_ret.begin(), normal_ret.end(), '-', '_');
  41. return normal_ret;
  42. }
  43. bool TbeKernelJsonCreator::GenTbeSingleKernelJson(const shared_ptr<mindspore::AnfNode> &anf_node,
  44. nlohmann::json *kernel_json) {
  45. MS_EXCEPTION_IF_NULL(anf_node);
  46. MS_EXCEPTION_IF_NULL(kernel_json);
  47. std::string op_name = AnfAlgo::GetCNodeName(anf_node);
  48. auto op_info_ptr = mindspore::kernel::OpLib::FindOp(op_name, OpImplyType::kTBE);
  49. MS_EXCEPTION_IF_NULL(op_info_ptr);
  50. (*kernel_json)["platform"] = "TBE";
  51. (*kernel_json)["gen_model"] = "single";
  52. (*kernel_json)["impl_path"] = op_info_ptr->impl_path();
  53. nlohmann::json op_info_json;
  54. if (op_info_ptr->impl_path().empty()) {
  55. tbe::TbeAdapter::NormalizeFuncName(&op_name);
  56. } else {
  57. op_name = op_info_ptr->kernel_name();
  58. }
  59. op_info_json["name"] = op_name;
  60. // generate inputs json
  61. nlohmann::json inputs_json;
  62. if (!GenTbeInputsJson(anf_node, op_info_ptr, &inputs_json)) {
  63. MS_LOG(ERROR) << "Anf Node [" << op_name << "] generate inputs json failed";
  64. return false;
  65. }
  66. op_info_json["inputs"] = inputs_json;
  67. // generate outputs json
  68. nlohmann::json outputs_json;
  69. if (!GenTbeOutputsJson(anf_node, op_info_ptr, &outputs_json)) {
  70. MS_LOG(ERROR) << "Anf Node [" << op_name << "] generate outputs json failed";
  71. return false;
  72. }
  73. op_info_json["outputs"] = outputs_json;
  74. // generate attrs json
  75. nlohmann::json attrs_json;
  76. (void)GenTbeAttrJson(anf_node, op_info_ptr, &attrs_json);
  77. op_info_json["attrs"] = attrs_json;
  78. std::string json_str = op_info_json.dump();
  79. size_t hash_id = std::hash<std::string>()(json_str);
  80. json_name_ = op_name + "_" + std::to_string(hash_id);
  81. json_info_ = json_str;
  82. if (creater_type_ == PREBUILD) {
  83. op_info_json["kernel_name"] = NormalizeFullScopeName(anf_node->fullname_with_scope());
  84. } else {
  85. op_info_json["kernel_name"] = json_name_;
  86. }
  87. (*kernel_json)["op_info"] = op_info_json;
  88. if (creater_type_ == SINGLE_BUILD) {
  89. TbeUtils::SaveJsonInfo(json_name_, json_info_);
  90. }
  91. MS_LOG(INFO) << "Operate type:" << creater_type_ << ", full scope name is :" << anf_node->fullname_with_scope()
  92. << ", json info name is : " << json_name_ << ", kernel json:" << kernel_json->dump();
  93. return true;
  94. }
  95. bool TbeKernelJsonCreator::GenInputDescJson(const shared_ptr<AnfNode> &anf_node, size_t real_input_index, bool value,
  96. const shared_ptr<OpIOInfo> &input_ptr, const string &op_input_name,
  97. size_t input_i, vector<nlohmann::json> *input_list) {
  98. MS_EXCEPTION_IF_NULL(anf_node);
  99. MS_EXCEPTION_IF_NULL(input_ptr);
  100. MS_EXCEPTION_IF_NULL(input_list);
  101. std::string op_name = AnfAlgo::GetCNodeName(anf_node);
  102. if (input_ptr->name() == "input_indices" && op_name == kTopKOpName) {
  103. TbeAdapter::GenTopKV2IndicesTensorInfo(anf_node, real_input_index, input_list, creater_type_);
  104. } else {
  105. // dtype : float16
  106. auto tensor_dtype =
  107. std::make_shared<TensorType>(TypeIdToType(AnfAlgo::GetInputDeviceDataType(anf_node, real_input_index)));
  108. MS_EXCEPTION_IF_NULL(tensor_dtype);
  109. std::string dtype = tensor_dtype->element()->ToString();
  110. dtype = tbe::DtypeToString(dtype);
  111. // format
  112. std::string format = AnfAlgo::GetInputFormat(anf_node, real_input_index);
  113. if (format == kOpFormat_DEFAULT) {
  114. format = kOpFormat_NCHW;
  115. } else if (format == kOpFormat_FRAC_Z) {
  116. format = kOpFormat_FRACTAL_Z;
  117. }
  118. nlohmann::json input_desc_json;
  119. input_desc_json["dtype"] = dtype;
  120. input_desc_json["name"] = op_input_name + std::to_string(input_i);
  121. auto ori_shape = AnfAlgo::GetPrevNodeOutputInferShape(anf_node, real_input_index);
  122. if (ori_shape.empty()) {
  123. ori_shape.emplace_back(1);
  124. }
  125. input_desc_json["ori_shape"] = ori_shape;
  126. input_desc_json["ori_format"] = kOpFormat_NCHW;
  127. auto shape = AnfAlgo::GetInputDeviceShape(anf_node, real_input_index);
  128. if (shape.empty()) {
  129. shape.emplace_back(1);
  130. }
  131. if (creater_type_ == OP_SELECT_FORMAT || creater_type_ == CHECK_SUPPORTED) {
  132. input_desc_json["shape"] = ori_shape;
  133. input_desc_json["format"] = kOpFormat_NCHW;
  134. } else {
  135. input_desc_json["shape"] = shape;
  136. input_desc_json["format"] = format;
  137. }
  138. input_desc_json["valid"] = value;
  139. input_list->emplace_back(input_desc_json);
  140. }
  141. return true;
  142. }
  143. bool TbeKernelJsonCreator::GenInputList(const shared_ptr<AnfNode> &anf_node, size_t input_tensor_num,
  144. const shared_ptr<OpIOInfo> &input_ptr, size_t *real_input_index,
  145. string *op_input_name, vector<nlohmann::json> *input_list) {
  146. MS_EXCEPTION_IF_NULL(anf_node);
  147. MS_EXCEPTION_IF_NULL(input_ptr);
  148. MS_EXCEPTION_IF_NULL(real_input_index);
  149. MS_EXCEPTION_IF_NULL(op_input_name);
  150. MS_EXCEPTION_IF_NULL(input_list);
  151. std::string op_name = AnfAlgo::GetCNodeName(anf_node);
  152. auto primitive = AnfAlgo::GetCNodePrimitive(anf_node);
  153. size_t real_input_num = AnfAlgo::GetInputTensorNum(anf_node);
  154. bool value = true;
  155. for (size_t input_i = 0; input_i < input_tensor_num; input_i++) {
  156. if (*real_input_index >= real_input_num) {
  157. if (input_ptr->param_type() == "optional") {
  158. *op_input_name = input_ptr->name() + "_optional_";
  159. nlohmann::json input_desc_json;
  160. input_desc_json["valid"] = false;
  161. input_desc_json["name"] = *op_input_name + std::to_string(*real_input_index);
  162. input_list->emplace_back(input_desc_json);
  163. continue;
  164. }
  165. MS_LOG(ERROR) << "input num: " << *real_input_index << " is not match op inputs";
  166. return false;
  167. }
  168. if (op_name == "BatchNorm") {
  169. if (input_ptr->name() == "mean" || input_ptr->name() == "variance") {
  170. auto attr = primitive->GetAttr("is_training");
  171. MS_EXCEPTION_IF_NULL(attr);
  172. bool is_training = GetValue<bool>(attr);
  173. MS_LOG(INFO) << "op_name" << op_name << ", tensor_name " << input_ptr->name() << ", is_training "
  174. << is_training;
  175. if (is_training) {
  176. (*real_input_index)++;
  177. break;
  178. }
  179. }
  180. }
  181. bool ret = GenInputDescJson(anf_node, *real_input_index, value, input_ptr, *op_input_name, input_i, input_list);
  182. (*real_input_index)++;
  183. if (!ret) {
  184. return false;
  185. }
  186. }
  187. return true;
  188. }
  189. bool GetInputNameAndRealNum(const std::shared_ptr<AnfNode> &anf_node, const shared_ptr<OpIOInfo> &input_ptr,
  190. size_t *dyn_input_index, size_t *input_num, std::string *op_input_name) {
  191. MS_EXCEPTION_IF_NULL(anf_node);
  192. MS_EXCEPTION_IF_NULL(input_ptr);
  193. MS_EXCEPTION_IF_NULL(dyn_input_index);
  194. MS_EXCEPTION_IF_NULL(input_num);
  195. MS_EXCEPTION_IF_NULL(op_input_name);
  196. auto primitive = AnfAlgo::GetCNodePrimitive(anf_node);
  197. // for dynamic input number, dyn_input_sizes has the info of dynamic input num for each input.
  198. std::vector<int> dyn_input_sizes;
  199. if (primitive->GetAttr(kAttrDynInputSizes) != nullptr) {
  200. dyn_input_sizes = GetValue<const std::vector<int>>(primitive->GetAttr(kAttrDynInputSizes));
  201. }
  202. if (input_ptr->param_type() == "dynamic") {
  203. if (*dyn_input_index >= dyn_input_sizes.size()) {
  204. MS_LOG(ERROR) << "dyn input index" << *dyn_input_index << "is over dyn input num" << dyn_input_sizes.size();
  205. return false;
  206. }
  207. *input_num = IntToSize(dyn_input_sizes[*dyn_input_index]);
  208. *op_input_name = input_ptr->name() + "_dynamic_";
  209. (*dyn_input_index)++;
  210. // if optional input is exist
  211. } else {
  212. *input_num = 1;
  213. *op_input_name = input_ptr->name() + "_";
  214. }
  215. return true;
  216. }
  217. bool TbeKernelJsonCreator::GenTbeInputsJson(const std::shared_ptr<AnfNode> &anf_node,
  218. const std::shared_ptr<OpInfo> &op_info, nlohmann::json *inputs_json) {
  219. MS_EXCEPTION_IF_NULL(anf_node);
  220. MS_EXCEPTION_IF_NULL(op_info);
  221. MS_EXCEPTION_IF_NULL(inputs_json);
  222. std::string op_name = AnfAlgo::GetCNodeName(anf_node);
  223. if (op_name == kAtomicAddrCleanOpName) {
  224. return true;
  225. }
  226. std::vector<std::shared_ptr<OpIOInfo>> inputs_ptr = op_info->inputs_ptr();
  227. if (inputs_ptr.empty()) {
  228. MS_LOG(INFO) << "Apply kernel " << op_name << "registration info has no input info";
  229. return true;
  230. }
  231. auto op_info_input_num = inputs_ptr.size();
  232. size_t dyn_input_index = 0;
  233. size_t real_input_index = 0;
  234. std::vector<std::vector<nlohmann::json>> inputs_list;
  235. for (size_t i = 0; i < op_info_input_num; i++) {
  236. size_t input_tensor_num;
  237. std::shared_ptr<OpIOInfo> input_ptr = inputs_ptr[i];
  238. std::string op_input_name;
  239. MS_EXCEPTION_IF_NULL(input_ptr);
  240. if (!GetInputNameAndRealNum(anf_node, input_ptr, &dyn_input_index, &input_tensor_num, &op_input_name)) {
  241. return false;
  242. }
  243. std::vector<nlohmann::json> input_list;
  244. if (!GenInputList(anf_node, input_tensor_num, input_ptr, &real_input_index, &op_input_name, &input_list)) {
  245. return false;
  246. }
  247. inputs_list.emplace_back(input_list);
  248. }
  249. TbeAdapter::InputOrderPass(op_name, inputs_list, inputs_json);
  250. return true;
  251. }
  252. bool TbeKernelJsonCreator::GenTbeOutputsJson(const std::shared_ptr<AnfNode> &anf_node,
  253. const std::shared_ptr<OpInfo> &op_info, nlohmann::json *outputs_json) {
  254. MS_EXCEPTION_IF_NULL(anf_node);
  255. MS_EXCEPTION_IF_NULL(op_info);
  256. MS_EXCEPTION_IF_NULL(outputs_json);
  257. auto op_name = AnfAlgo::GetCNodeName(anf_node);
  258. if (op_name == kAtomicAddrCleanOpName) {
  259. return true;
  260. }
  261. auto outputs_ptr = op_info->outputs_ptr();
  262. return GenOutputDescJson(anf_node, outputs_ptr, outputs_json);
  263. }
  264. bool TbeKernelJsonCreator::GenOutputDescJson(const shared_ptr<mindspore::AnfNode> &anf_node,
  265. const vector<shared_ptr<mindspore::kernel::OpIOInfo>> &outputs_ptr,
  266. nlohmann::json *outputs_json) {
  267. MS_EXCEPTION_IF_NULL(outputs_json);
  268. size_t output_idx = 0;
  269. auto op_name = AnfAlgo::GetCNodeName(anf_node);
  270. size_t real_output_num = AnfAlgo::GetOutputTensorNum(anf_node);
  271. for (const auto &output_ptr : outputs_ptr) {
  272. size_t output_obj_num = 0;
  273. if (output_ptr->param_type() == "required") {
  274. output_obj_num = 1;
  275. } else if (output_ptr->param_type() == "dynamic") {
  276. if (outputs_ptr.size() > 1) {
  277. MS_LOG(ERROR) << "Dynamic output is unsupported multi output!";
  278. return false;
  279. }
  280. output_obj_num = real_output_num;
  281. } else {
  282. if (output_idx >= real_output_num) {
  283. MS_LOG(INFO) << "op:" << op_name << ", output" << output_ptr->name() << " is optional, output is none.";
  284. std::vector<nlohmann::json> output_list;
  285. nlohmann::json output_obj;
  286. output_obj["name"] = output_ptr->name();
  287. output_obj["valid"] = false;
  288. output_list.emplace_back(output_obj);
  289. (*outputs_json).push_back(output_list);
  290. continue;
  291. } else {
  292. output_obj_num = 1;
  293. }
  294. }
  295. std::vector<nlohmann::json> output_list;
  296. GenOutputList(anf_node, output_obj_num, output_ptr, &output_idx, &output_list);
  297. (*outputs_json).push_back(output_list);
  298. }
  299. return true;
  300. }
  301. void TbeKernelJsonCreator::GenOutputList(const shared_ptr<AnfNode> &anf_node, const size_t &output_obj_num,
  302. const shared_ptr<OpIOInfo> &output_ptr, size_t *output_idx,
  303. vector<nlohmann::json> *output_list) {
  304. MS_EXCEPTION_IF_NULL(output_idx);
  305. MS_EXCEPTION_IF_NULL(output_list);
  306. for (size_t i = 0; i < output_obj_num; i++) {
  307. nlohmann::json output_obj;
  308. auto type_ptr = std::make_shared<TensorType>(TypeIdToType(AnfAlgo::GetOutputDeviceDataType(anf_node, *output_idx)));
  309. std::string dtype = type_ptr->element()->ToString();
  310. dtype = tbe::DtypeToString(dtype);
  311. std::string format = AnfAlgo::GetOutputFormat(anf_node, *output_idx);
  312. if (format == kOpFormat_DEFAULT) {
  313. format = kOpFormat_NCHW;
  314. } else if (format == kOpFormat_FRAC_Z) {
  315. format = kOpFormat_FRACTAL_Z;
  316. }
  317. std::vector<size_t> ori_shape;
  318. if (AnfAlgo::GetOutputInferShape(anf_node, *output_idx).empty()) {
  319. ori_shape.emplace_back(1);
  320. } else {
  321. ori_shape = AnfAlgo::GetOutputInferShape(anf_node, *output_idx);
  322. }
  323. output_obj["dtype"] = dtype;
  324. auto shape = AnfAlgo::GetOutputDeviceShape(anf_node, *output_idx);
  325. if (shape.empty()) {
  326. shape.emplace_back(1);
  327. }
  328. if (creater_type_ == OP_SELECT_FORMAT || creater_type_ == CHECK_SUPPORTED) {
  329. output_obj["shape"] = ori_shape;
  330. output_obj["format"] = kOpFormat_NCHW;
  331. } else {
  332. output_obj["shape"] = shape;
  333. output_obj["format"] = format;
  334. }
  335. output_obj["ori_shape"] = ori_shape;
  336. output_obj["ori_format"] = kOpFormat_NCHW;
  337. output_obj["name"] = output_ptr->name();
  338. output_obj["valid"] = true;
  339. output_list->emplace_back(output_obj);
  340. (*output_idx)++;
  341. }
  342. }
  343. bool TbeKernelJsonCreator::GenTbeAttrJson(const std::shared_ptr<AnfNode> &anf_node,
  344. const std::shared_ptr<OpInfo> &op_info, nlohmann::json *attrs_json) {
  345. MS_EXCEPTION_IF_NULL(anf_node);
  346. MS_EXCEPTION_IF_NULL(op_info);
  347. MS_EXCEPTION_IF_NULL(attrs_json);
  348. auto attrs_ptr = op_info->attrs_ptr();
  349. std::string op_name = AnfAlgo::GetCNodeName(anf_node);
  350. if (TbeAdapter::RunAttrPass(anf_node, attrs_ptr, attrs_json)) {
  351. return true;
  352. }
  353. auto primitive = AnfAlgo::GetCNodePrimitive(anf_node);
  354. MS_EXCEPTION_IF_NULL(primitive);
  355. for (const auto &attr_ptr : attrs_ptr) {
  356. std::string attr_name = attr_ptr->name();
  357. nlohmann::json attr_obj;
  358. attr_obj["name"] = attr_name;
  359. if (op_name == "LayerNorm" && attr_obj["name"] == "epsilon" && creater_type_ == OP_SELECT_FORMAT) {
  360. continue;
  361. }
  362. if (primitive->GetAttr(attr_name) != nullptr) {
  363. auto value = primitive->GetAttr(attr_name);
  364. std::string type = attr_ptr->type();
  365. ParseAttrValue(type, value, &attr_obj);
  366. attr_obj["valid"] = true;
  367. } else {
  368. if (op_info->impl_path().empty()) {
  369. attr_obj["valid"] = false;
  370. } else {
  371. if (attr_ptr->param_type() == "required" && creater_type_ == SINGLE_BUILD) {
  372. MS_LOG(EXCEPTION) << "op name: " << op_info->op_name() << " attr: " << attr_name
  373. << " is required, but not set.";
  374. } else {
  375. attr_obj["valid"] = false;
  376. }
  377. }
  378. }
  379. (*attrs_json).push_back(attr_obj);
  380. }
  381. return true;
  382. }
  383. void TbeKernelJsonCreator::ParseAttrValue(const std::string &type, const mindspore::ValuePtr &value,
  384. nlohmann::json *attr_obj) {
  385. MS_EXCEPTION_IF_NULL(value);
  386. MS_EXCEPTION_IF_NULL(attr_obj);
  387. if (type == "int") {
  388. auto attr_value = GetValue<int>(value);
  389. (*attr_obj)["value"] = attr_value;
  390. } else if (type == "str") {
  391. auto attr_value = GetValue<std::string>(value);
  392. if (attr_value == kOpFormat_FRAC_Z) {
  393. attr_value = kOpFormat_FRACTAL_Z;
  394. }
  395. (*attr_obj)["value"] = attr_value;
  396. } else if (type == "bool") {
  397. auto attr_value = GetValue<bool>(value);
  398. (*attr_obj)["value"] = attr_value;
  399. } else if (type == "float") {
  400. auto attr_value = GetValue<float>(value);
  401. (*attr_obj)["value"] = attr_value;
  402. } else if (type == "listInt") {
  403. std::vector<int> attr_value;
  404. auto value_type = value->type();
  405. MS_EXCEPTION_IF_NULL(value_type);
  406. auto value_type_str = value_type->ToString();
  407. if (value_type_str == "Int32") {
  408. int data = GetValue<int>(value);
  409. attr_value.push_back(data);
  410. } else {
  411. attr_value = GetValue<std::vector<int>>(value);
  412. }
  413. (*attr_obj)["value"] = attr_value;
  414. } else if (type == "listFloat") {
  415. std::vector<float> attr_value;
  416. auto value_type = value->type();
  417. MS_EXCEPTION_IF_NULL(value_type);
  418. auto value_type_str = value_type->ToString();
  419. if (value_type_str == "float") {
  420. auto data = GetValue<float>(value);
  421. attr_value.push_back(data);
  422. } else {
  423. attr_value = GetValue<std::vector<float>>(value);
  424. }
  425. (*attr_obj)["value"] = attr_value;
  426. } else if (type == "listListInt") {
  427. auto attr_value = GetValue<std::vector<std::vector<int>>>(value);
  428. (*attr_obj)["value"] = attr_value;
  429. } else {
  430. MS_LOG(EXCEPTION) << "type: " << type << "not support";
  431. }
  432. }
  433. bool TbeKernelBuild::GetIOSize(const nlohmann::json &kernel_json, std::vector<size_t> *input_size_list,
  434. std::vector<size_t> *output_size_list) {
  435. if (input_size_list == nullptr || output_size_list == nullptr) {
  436. MS_LOG(ERROR) << "input size or output size is nullptr";
  437. return false;
  438. }
  439. input_size_list->clear();
  440. output_size_list->clear();
  441. for (size_t i = 0; i < kernel_json["op_info"]["inputs"].size(); i++) {
  442. for (size_t m = 0; m < kernel_json["op_info"]["inputs"][i].size(); m++) {
  443. size_t size_i = 1;
  444. if (kernel_json["op_info"]["inputs"][i][m]["valid"] == false) {
  445. std::string input_name = kernel_json["op_info"]["inputs"][i][m]["name"];
  446. MS_LOG(INFO) << "Input name:" << input_name << "is optional, valid is false.";
  447. continue;
  448. }
  449. for (const auto &j : kernel_json["op_info"]["inputs"][i][m]["shape"]) {
  450. size_i *= static_cast<size_t>(j);
  451. }
  452. std::string dtype = kernel_json["op_info"]["inputs"][i][m]["dtype"];
  453. size_t nbyte = tbe::GetDtypeNbyte(dtype);
  454. size_i *= nbyte;
  455. input_size_list->push_back(size_i);
  456. }
  457. }
  458. for (size_t i = 0; i < kernel_json["op_info"]["outputs"].size(); i++) {
  459. for (size_t m = 0; m < kernel_json["op_info"]["outputs"][i].size(); m++) {
  460. size_t size_i = 1;
  461. if (kernel_json["op_info"]["outputs"][i][m]["valid"] == false) {
  462. std::string output_name = kernel_json["op_info"]["outputs"][i][m]["name"];
  463. MS_LOG(INFO) << "Output name:" << output_name << " is optional, valid is false.";
  464. continue;
  465. }
  466. for (const auto &j : kernel_json["op_info"]["outputs"][i][m]["shape"]) {
  467. size_i *= static_cast<size_t>(j);
  468. }
  469. std::string dtype = kernel_json["op_info"]["outputs"][i][m]["dtype"];
  470. size_t nbyte = tbe::GetDtypeNbyte(dtype);
  471. size_i *= nbyte;
  472. output_size_list->push_back(size_i);
  473. }
  474. }
  475. return true;
  476. }
  477. bool TbeKernelBuild::GenFusionScopeJson(const vector<mindspore::AnfNodePtr> &input_nodes,
  478. const vector<mindspore::AnfNodePtr> &compute_nodes, nlohmann::json *fusion_str,
  479. std::string *fusion_kernel) {
  480. MS_EXCEPTION_IF_NULL(fusion_str);
  481. MS_EXCEPTION_IF_NULL(fusion_kernel);
  482. // get input layer info
  483. std::vector<std::vector<mindspore::AnfNodePtr>> input_layers;
  484. std::map<const AnfNodePtr, FusionDataType> spec_data_input;
  485. if (!GetInputLayers(input_nodes, compute_nodes, &input_layers, &spec_data_input)) {
  486. return false;
  487. }
  488. // gen fusion scopre_op jsom
  489. vector<nlohmann::json> compute_list;
  490. (*fusion_kernel) = kFusionKernelNamePrfix;
  491. // index: fusion build option input record, next one from 0
  492. static size_t index = 0;
  493. auto layer_iter = input_layers.begin();
  494. auto compute_op_iter = compute_nodes.begin();
  495. for (; compute_op_iter != compute_nodes.end(); ++compute_op_iter, ++layer_iter) {
  496. nlohmann::json compute_op_str;
  497. (void)GenFusionComputeJson(*compute_op_iter, &layer_iter, &compute_op_str, fusion_kernel, &index);
  498. compute_list.push_back(compute_op_str);
  499. }
  500. index = 0;
  501. // gen data input json
  502. vector<nlohmann::json> data_list;
  503. for (const auto &layer : input_layers) {
  504. for (const auto &data_input : layer) {
  505. nlohmann::json data_str;
  506. if (!GenFusionDataInputJson(data_input, spec_data_input, &data_str, &index)) {
  507. MS_LOG(INFO) << "Fusion error: gen fusion datainput json faild.";
  508. return false;
  509. }
  510. data_list.push_back(data_str);
  511. }
  512. }
  513. index = 0;
  514. data_list.insert(data_list.end(), compute_list.begin(), compute_list.end());
  515. (*fusion_str)[kFusionOpList] = data_list;
  516. return true;
  517. }
  518. void TbeKernelBuild::GenDescJson(const std::shared_ptr<mindspore::AnfNode> &anf_node, size_t node_out_idx,
  519. size_t desc_output_idx, nlohmann::json *output_desc, FusionDataType fusion_data_type) {
  520. std::string output_desc_name = anf_node->fullname_with_scope();
  521. if (node_out_idx > 0) {
  522. output_desc_name = output_desc_name + "_" + std::to_string(node_out_idx);
  523. }
  524. (*output_desc)["name"] = NormalizeFullScopeName(output_desc_name);
  525. auto type_id = AnfAlgo::GetOutputDeviceDataType(anf_node, node_out_idx);
  526. (*output_desc)["data_type"] = tbe::TypeIdToString(type_id);
  527. auto ori_shape = AnfAlgo::GetOutputInferShape(anf_node, node_out_idx);
  528. if (ori_shape.empty()) {
  529. ori_shape.emplace_back(1);
  530. }
  531. (*output_desc)["ori_shape"] = ori_shape;
  532. auto shape = AnfAlgo::GetOutputDeviceShape(anf_node, node_out_idx);
  533. if (shape.empty()) {
  534. shape.emplace_back(1);
  535. }
  536. (*output_desc)["shape"] = shape;
  537. auto format = AnfAlgo::GetOutputFormat(anf_node, node_out_idx);
  538. if (format == kOpFormat_DEFAULT) {
  539. format = ori_shape.size() == 4 ? kOpFormat_NCHW : kOpFormat_ND;
  540. }
  541. (*output_desc)["format"] = format;
  542. (*output_desc)["ori_format"] = kOpFormat_NCHW;
  543. (*output_desc)["output_index"] = desc_output_idx;
  544. if (fusion_data_type == kFusionAddN && format == kOpFormat_NC1HWC0) {
  545. std::vector<size_t> spec_shape = {};
  546. spec_shape.emplace_back(shape[0]);
  547. spec_shape.emplace_back(shape[1]);
  548. spec_shape.emplace_back(shape[2] * shape[3]);
  549. spec_shape.emplace_back(shape[4]);
  550. (*output_desc)["shape"] = spec_shape;
  551. } else if (fusion_data_type == kFusionReLUGradV2 && (*output_desc)["data_type"] == "uint8") {
  552. std::vector<size_t> spec_shape = {};
  553. spec_shape.emplace_back(shape[0]);
  554. spec_shape.emplace_back(shape[1]);
  555. spec_shape.emplace_back(shape[2] * shape[3]);
  556. spec_shape.emplace_back(16);
  557. (*output_desc)["shape"] = spec_shape;
  558. (*output_desc)["data_type"] = "bool";
  559. }
  560. }
  561. void TbeKernelBuild::GenReusedOutputDesc(const shared_ptr<mindspore::AnfNode> &anf_node, size_t index,
  562. size_t output_index, nlohmann::json *output_desc) {
  563. std::string output_desc_name = anf_node->fullname_with_scope() + "_" + std::to_string(index);
  564. (*output_desc)["name"] = NormalizeFullScopeName(output_desc_name);
  565. (*output_desc)["output_index"] = output_index;
  566. std::vector<size_t> shape;
  567. (*output_desc)["shape"] = shape;
  568. }
  569. bool TbeKernelBuild::GetSpecInputLayers(const std::string &op_name,
  570. const std::vector<mindspore::AnfNodePtr> &reorder_layer,
  571. std::map<const AnfNodePtr, FusionDataType> *spec_data_input) {
  572. if ((op_name == kReluGradV2OpName || op_name == kAddNOpName) && reorder_layer.empty()) {
  573. MS_LOG(INFO) << "Fusion error: node(" << op_name << " )'s input is null. ";
  574. return false;
  575. }
  576. MS_LOG(INFO) << "Fusion info: op_name: " << op_name << "input layer size: " << reorder_layer.size();
  577. if (op_name == kReluGradV2OpName) {
  578. (*spec_data_input)[reorder_layer[0]] = kFusionReLUGradV2;
  579. } else if (op_name == kAddNOpName) {
  580. for (const auto &it : reorder_layer) {
  581. (*spec_data_input)[it] = kFusionAddN;
  582. }
  583. }
  584. return true;
  585. }
  586. bool TbeKernelBuild::GetInputLayers(const std::vector<mindspore::AnfNodePtr> &input_nodes,
  587. const std::vector<mindspore::AnfNodePtr> &compute_nodes,
  588. std::vector<std::vector<mindspore::AnfNodePtr>> *input_layers,
  589. std::map<const AnfNodePtr, FusionDataType> *spec_data_input) {
  590. auto result = std::find_if(compute_nodes.begin(), compute_nodes.end(), [](const auto &it) {
  591. auto op_name = AnfAlgo::GetCNodeName(it);
  592. return op_name == kConv2DBackpropInputOpName;
  593. });
  594. bool need_spec = (result != compute_nodes.end());
  595. size_t input_size = 0;
  596. for (const auto &compute_node : compute_nodes) {
  597. std::vector<mindspore::AnfNodePtr> layer = {};
  598. std::vector<mindspore::AnfNodePtr> reorder_layer = {};
  599. MS_EXCEPTION_IF_NULL(compute_node);
  600. auto op_name = AnfAlgo::GetCNodeName(compute_node);
  601. auto ccompute_node = compute_node->cast<CNodePtr>();
  602. if (ccompute_node == nullptr) {
  603. MS_LOG(INFO) << "Fusion error: fusion compute node must be cnode";
  604. return false;
  605. }
  606. MS_LOG(INFO) << "Fusion info: compute name: " << compute_node->fullname_with_scope();
  607. for (size_t i = 1; i < ccompute_node->inputs().size(); ++i) {
  608. auto input = ccompute_node->input(i);
  609. auto find_iter = std::find(input_nodes.begin(), input_nodes.end(), input);
  610. if (find_iter != input_nodes.end()) {
  611. MS_LOG(INFO) << "Fusion info: add compute node's [" << i << "] input: " << input->fullname_with_scope();
  612. layer.emplace_back((*find_iter));
  613. } else {
  614. MS_LOG(INFO) << "Fusion warnig: this input [" << i << "] may be pre compute(" << input->fullname_with_scope()
  615. << ") node's output.";
  616. }
  617. }
  618. TbeAdapter::FusionDataOrderPass(op_name, layer, &reorder_layer);
  619. if (need_spec) {
  620. MS_LOG(INFO) << "Fusion info: match conv2d backprop input + ... patten.";
  621. if (!GetSpecInputLayers(op_name, reorder_layer, spec_data_input)) {
  622. return false;
  623. }
  624. }
  625. input_size += reorder_layer.size();
  626. input_layers->emplace_back(reorder_layer);
  627. }
  628. if (input_nodes.size() != input_size) {
  629. MS_LOG(INFO) << "Fusion error: fusion scope error, layer input:" << input_size
  630. << ", input_node:" << input_nodes.size();
  631. return false;
  632. }
  633. return true;
  634. }
  635. bool TbeKernelBuild::GenFusionDataInputJson(const std::shared_ptr<mindspore::AnfNode> &data_input,
  636. const std::map<const AnfNodePtr, FusionDataType> &spec_data_input,
  637. nlohmann::json *data_str, size_t *index) {
  638. MS_EXCEPTION_IF_NULL(data_str);
  639. MS_EXCEPTION_IF_NULL(index);
  640. std::vector<nlohmann::json> output_desc_list;
  641. if (!data_input) {
  642. MS_LOG(INFO) << "data input is optional node";
  643. auto name = std::string(kOptional) + std::to_string(*index);
  644. (*data_str)["name"] = name;
  645. nlohmann::json output_desc;
  646. output_desc["name"] = name;
  647. output_desc["shape"] = "NULL";
  648. output_desc_list.push_back(output_desc);
  649. (*index)++;
  650. } else {
  651. FusionDataType fusion_data_type = kFusionNormal;
  652. if (spec_data_input.find(data_input) != spec_data_input.end()) {
  653. fusion_data_type = spec_data_input.at(data_input);
  654. }
  655. auto kernel_idx = AnfAlgo::VisitKernel(data_input, 0);
  656. auto real_node = kernel_idx.first;
  657. size_t real_idx = kernel_idx.second;
  658. MS_LOG(INFO) << "real name " << real_node->fullname_with_scope() << " index:" << real_idx;
  659. // "output_desc"
  660. nlohmann::json output_desc;
  661. GenDescJson(real_node, real_idx, real_idx, &output_desc, fusion_data_type);
  662. output_desc_list.push_back(output_desc);
  663. (*data_str)["name"] = NormalizeFullScopeName(real_node->fullname_with_scope());
  664. }
  665. (*data_str)["output_desc"] = output_desc_list;
  666. (*data_str)["type"] = "Data";
  667. return true;
  668. }
  669. bool TbeKernelBuild::IsDynamicInput(const mindspore::CNodePtr &cnode) {
  670. MS_EXCEPTION_IF_NULL(cnode);
  671. auto primitive = AnfAlgo::GetCNodePrimitive(cnode);
  672. MS_EXCEPTION_IF_NULL(primitive);
  673. // for dynamic input number, dyn_input_sizes has the info of dynamic input num for each input.
  674. bool ret = false;
  675. std::vector<int> dyn_input_sizes;
  676. auto dynamic_input_attr = primitive->GetAttr(kAttrDynInputSizes);
  677. if (dynamic_input_attr != nullptr) {
  678. dyn_input_sizes = GetValue<const std::vector<int>>(dynamic_input_attr);
  679. auto real_input_size = cnode->inputs().size() - 1;
  680. auto dyn_input_size = dyn_input_sizes.size();
  681. if (dyn_input_size != 1) {
  682. MS_LOG(INFO) << "Fusion error: fusion build not support dyn_input_sizes > 1";
  683. return ret;
  684. }
  685. if (IntToSize(dyn_input_sizes[0]) != real_input_size) {
  686. MS_LOG(INFO) << "Fusion error: dyn_input_size" << dyn_input_sizes[0] << "not equal real_input_size"
  687. << real_input_size;
  688. return ret;
  689. }
  690. ret = true;
  691. }
  692. return ret;
  693. }
  694. size_t TbeKernelBuild::GetOptionalInput(const mindspore::CNodePtr &cnode, bool is_dynamic_input) {
  695. if (is_dynamic_input) {
  696. return 0;
  697. }
  698. MS_EXCEPTION_IF_NULL(cnode);
  699. auto node_name = AnfAlgo::GetCNodeName(cnode);
  700. auto op_info = OpLib::FindOp(node_name, kTBE);
  701. MS_EXCEPTION_IF_NULL(cnode);
  702. if (op_info->inputs_ptr().size() < (cnode->inputs().size() - 1)) {
  703. MS_EXCEPTION(ArgumentError) << "op info error, node name:" << cnode->fullname_with_scope();
  704. }
  705. return (op_info->inputs_ptr().size() + 1 - cnode->inputs().size());
  706. }
  707. std::string TbeKernelBuild::GetRealOpType(const std::string &origin_type) {
  708. static std::map<std::string, std::string> buffer_fussion_op_map = {{"DepthwiseConv2dNative", "DepthwiseConv2D"},
  709. {"TensorAdd", "Add"}};
  710. string result = origin_type;
  711. auto iter = buffer_fussion_op_map.find(origin_type);
  712. if (iter != buffer_fussion_op_map.end()) {
  713. result = iter->second;
  714. }
  715. return result;
  716. }
  717. bool TbeKernelBuild::GenFusionComputeInputJson(const mindspore::CNodePtr &cnode,
  718. std::vector<std::vector<mindspore::AnfNodePtr>>::iterator *layer_iter,
  719. std::vector<nlohmann::json> *input_desc_list, size_t *index) {
  720. MS_EXCEPTION_IF_NULL(cnode);
  721. MS_EXCEPTION_IF_NULL(input_desc_list);
  722. std::vector<nlohmann::json> input_desc_list_tmp = {};
  723. bool is_dynamic_input = IsDynamicInput(cnode);
  724. for (size_t i = 1; i < cnode->inputs().size(); ++i) {
  725. auto input = cnode->input(i);
  726. auto kernel_idx = AnfAlgo::VisitKernel(input, 0);
  727. auto real_node = kernel_idx.first;
  728. size_t real_idx = kernel_idx.second;
  729. MS_LOG(INFO) << "real name" << real_node->fullname_with_scope() << "index:" << real_idx;
  730. nlohmann::json input_desc;
  731. GenDescJson(real_node, real_idx, real_idx, &input_desc);
  732. if (is_dynamic_input) {
  733. MS_LOG(INFO) << "node has dynamic input.";
  734. input_desc["dyn_index"] = (i - 1);
  735. }
  736. input_desc_list_tmp.emplace_back(input_desc);
  737. }
  738. size_t optional_num = GetOptionalInput(cnode, is_dynamic_input);
  739. if (optional_num > 0) {
  740. MS_LOG(INFO) << "node has optional input.";
  741. for (size_t i = 0; i < optional_num; ++i) {
  742. nlohmann::json optional_input_desc;
  743. optional_input_desc["name"] = std::string(kOptional) + std::to_string(*index);
  744. (*index)++;
  745. (*layer_iter)->emplace_back(nullptr);
  746. input_desc_list_tmp.emplace_back(optional_input_desc);
  747. }
  748. }
  749. auto op_name = AnfAlgo::GetCNodeName(cnode);
  750. TbeAdapter::FusionInputOrderPass(op_name, input_desc_list_tmp, input_desc_list);
  751. return true;
  752. }
  753. std::vector<size_t> TbeKernelBuild::GetDescOutputIndex(const std::vector<int> &output_used_nums) {
  754. std::vector<size_t> desc_output_index = {};
  755. for (size_t idx = 0; idx < output_used_nums.size(); ++idx) {
  756. auto output_use_num_item = output_used_nums[idx];
  757. MS_LOG(INFO) << "output used num[" << idx << "] = " << output_use_num_item;
  758. desc_output_index.emplace_back(idx);
  759. if (output_use_num_item > 1) {
  760. desc_output_index.emplace_back(idx);
  761. }
  762. }
  763. return desc_output_index;
  764. }
  765. bool TbeKernelBuild::GenFusionComputeOutputJson(const mindspore::CNodePtr &cnode,
  766. std::vector<nlohmann::json> *output_desc_list) {
  767. auto output_size = AnfAlgo::GetOutputTensorNum(cnode);
  768. if (AnfAlgo::HasNodeAttr(kAttrOutputUsedNum, cnode)) {
  769. auto output_used_nums = AnfAlgo::GetNodeAttr<std::vector<int>>(cnode, kAttrOutputUsedNum);
  770. MS_LOG(INFO) << "This node's output has been reused, node name: " << cnode->fullname_with_scope();
  771. if (output_used_nums.size() != output_size) {
  772. MS_LOG(INFO) << "Fusion error: output tenor num(" << output_size << ")"
  773. << " is not match output used num(" << output_used_nums.size() << ")";
  774. return false;
  775. }
  776. auto desc_output_index = GetDescOutputIndex(output_used_nums);
  777. for (size_t i = 0; i < output_size; ++i) {
  778. MS_LOG(INFO) << "Fusion index: " << i << ", desc_output_index: " << desc_output_index[i];
  779. nlohmann::json output_desc;
  780. GenDescJson(cnode, i, desc_output_index[i], &output_desc);
  781. output_desc_list->emplace_back(output_desc);
  782. }
  783. for (size_t j = output_size; j < desc_output_index.size(); ++j) {
  784. MS_LOG(INFO) << "Fusion index: " << j << ", desc_output_index: " << desc_output_index[j];
  785. nlohmann::json output_desc;
  786. GenReusedOutputDesc(cnode, j, desc_output_index[j], &output_desc);
  787. output_desc_list->emplace_back(output_desc);
  788. }
  789. } else {
  790. for (size_t i = 0; i < output_size; ++i) {
  791. nlohmann::json output_desc;
  792. GenDescJson(cnode, i, i, &output_desc);
  793. output_desc_list->push_back(output_desc);
  794. }
  795. }
  796. return true;
  797. }
  798. bool TbeKernelBuild::GenFusionComputeJson(const mindspore::AnfNodePtr &compute_node,
  799. std::vector<std::vector<mindspore::AnfNodePtr>>::iterator *layer_iter,
  800. nlohmann::json *compute_op_str, std::string *fusion_kernel_name,
  801. size_t *index) {
  802. MS_EXCEPTION_IF_NULL(compute_node);
  803. auto cnode = compute_node->cast<CNodePtr>();
  804. MS_EXCEPTION_IF_NULL(cnode);
  805. // gen input desc
  806. std::vector<nlohmann::json> input_desc_list;
  807. (void)GenFusionComputeInputJson(cnode, layer_iter, &input_desc_list, index);
  808. (*compute_op_str)["input_desc"] = input_desc_list;
  809. // gen output desc
  810. std::vector<nlohmann::json> output_desc_list;
  811. if (!GenFusionComputeOutputJson(cnode, &output_desc_list)) {
  812. MS_LOG(INFO) << "Fusion Error: gen fusion output desc faild, node full name: " << cnode->fullname_with_scope();
  813. return false;
  814. }
  815. (*compute_op_str)["output_desc"] = output_desc_list;
  816. // gen others
  817. auto origin_type = AnfAlgo::GetCNodeName(cnode);
  818. // replace special op type for buffer fusion op
  819. auto type = GetRealOpType(origin_type);
  820. (*compute_op_str)["type"] = type;
  821. tbe::TbeAdapter::NormalizeFuncName(&type);
  822. (*compute_op_str)["func_name"] = type;
  823. (*compute_op_str)["name"] = NormalizeFullScopeName(cnode->fullname_with_scope());
  824. (void)(*fusion_kernel_name).append("_");
  825. (void)(*fusion_kernel_name).append(type);
  826. return true;
  827. }
  828. size_t TbeKernelBuild::GetIOSizeImpl(const nlohmann::json &desc) {
  829. size_t ret = 1;
  830. for (const auto &shape_item : desc["shape"]) {
  831. ret *= static_cast<size_t>(shape_item);
  832. }
  833. std::string data_type = desc["data_type"];
  834. size_t nbyte = tbe::GetDtypeNbyte(data_type);
  835. ret *= nbyte;
  836. return ret;
  837. }
  838. bool TbeKernelBuild::GetIOSize(const nlohmann::json &fusion_op_list, const vector<mindspore::AnfNodePtr> &output_nodes,
  839. std::vector<size_t> *input_size_list, std::vector<size_t> *output_size_list) {
  840. MS_EXCEPTION_IF_NULL(input_size_list);
  841. MS_EXCEPTION_IF_NULL(output_size_list);
  842. input_size_list->clear();
  843. output_size_list->clear();
  844. for (const auto &op : fusion_op_list) {
  845. if (op["type"] == "Data") {
  846. const auto &data_output_desc = op["output_desc"];
  847. for (const auto &data_output : data_output_desc) {
  848. if (data_output["shape"] == "NULL") {
  849. break;
  850. }
  851. auto ret = GetIOSizeImpl(data_output);
  852. input_size_list->push_back(ret);
  853. MS_LOG(INFO) << "Fusion info: scope input name: " << op["name"] << ", size: " << ret;
  854. }
  855. }
  856. }
  857. for (const auto &output_node : output_nodes) {
  858. auto kernel_idx = AnfAlgo::VisitKernel(output_node, 0);
  859. auto real_node = kernel_idx.first;
  860. size_t real_idx = kernel_idx.second;
  861. auto normal_name = NormalizeFullScopeName(real_node->fullname_with_scope());
  862. MS_LOG(INFO) << "Fusion info: real node name: " << normal_name << ", real output index: " << real_idx;
  863. for (const auto &op : fusion_op_list) {
  864. if (op["name"] == normal_name) {
  865. auto op_output_desces = op["output_desc"];
  866. if (output_node != real_node) {
  867. // tuple_get item
  868. MS_LOG(INFO) << "output is a tuple getitem node";
  869. auto output_desc = op_output_desces[real_idx];
  870. if (output_desc["shape"].empty()) {
  871. MS_LOG(INFO) << "Fusion error: output_desc's shape is empty. real_index " << real_idx;
  872. return false;
  873. }
  874. auto ret = GetIOSizeImpl(output_desc);
  875. output_size_list->push_back(ret);
  876. MS_LOG(INFO) << "Fusion info: scope output index: " << real_idx << ", size: " << ret;
  877. } else {
  878. for (const auto &output_desc : op_output_desces) {
  879. if (output_desc["shape"].empty()) {
  880. MS_LOG(INFO) << "Fusion info: output_desc's shape is empty, may be this node output";
  881. continue;
  882. }
  883. auto ret = GetIOSizeImpl(output_desc);
  884. output_size_list->push_back(ret);
  885. MS_LOG(INFO) << "Fusion info: scope output size: " << ret;
  886. }
  887. }
  888. }
  889. }
  890. }
  891. return true;
  892. }
  893. } // namespace kernel
  894. } // namespace mindspore