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.

executor_opr.cpp 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /**
  2. * \file src/jit/impl/executor_opr.cpp
  3. * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  4. *
  5. * Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  6. *
  7. * Unless required by applicable law or agreed to in writing,
  8. * software distributed under the License is distributed on an
  9. * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. */
  11. #include "megbrain/jit/executor_opr.h"
  12. #include "megbrain/common.h"
  13. #include "megbrain/comp_node_env.h"
  14. #include "megbrain/gopt/framework.h"
  15. #include "megbrain/graph/grad_impl.h"
  16. #include "megbrain/graph/helper.h"
  17. #include "megbrain/jit/compiler.h"
  18. #include "megbrain/jit/param_elem_visitor.h"
  19. #include "megbrain/jit/placeholder_opr.h"
  20. #include "megbrain/opr/basic_arith_wrapper.h"
  21. #include "megbrain/opr/tensor_manip.h"
  22. #include "megbrain/opr/utility.h"
  23. #include "megbrain/utils/hash.h"
  24. #include "megbrain/serialization/opr_shallow_copy.h"
  25. #if MGB_JIT
  26. using namespace mgb;
  27. using namespace jit;
  28. using CPFlag = Compiler::Property::Flag;
  29. /* =================== Fusion ==================== */
  30. MGB_DYN_TYPE_OBJ_FINAL_IMPL(JITExecutor);
  31. JITExecutor::JITExecutor(const InternalGraphPtr& internal_graph,
  32. const VarNodeArray& inputs,
  33. const OperatorNodeConfig& config)
  34. : Super(internal_graph->output()->owner_graph(), config,
  35. ssprintf("JIT-Fusion{%zu}",
  36. internal_graph->placeholders().size()),
  37. inputs),
  38. m_internal_graph{internal_graph},
  39. m_compiler{Compiler::get(*inputs[0]->owner_graph(),
  40. inputs[0]->comp_node())} {
  41. for (auto inp : inputs) {
  42. add_input({inp});
  43. }
  44. m_input_broadcastable.resize(inputs.size());
  45. auto&& placeholders = m_internal_graph->placeholders();
  46. mgb_assert(placeholders.size() == inputs.size());
  47. for (size_t i = 0; i < inputs.size(); ++i) {
  48. mgb_assert(placeholders[i]->output(0) != internal_graph->output());
  49. if (placeholders[i]->is_host_value_shape_input() ||
  50. input()[i]
  51. ->owner_opr()
  52. ->same_type<opr::MarkNoBroadcastElemwise>()) {
  53. m_input_broadcastable[i] = false;
  54. } else {
  55. m_input_broadcastable[i] = true;
  56. }
  57. }
  58. if (inputs.size() == 1) {
  59. m_input_broadcastable[0] = false;
  60. } else {
  61. Maybe<size_t> non_scalar;
  62. for (size_t i = 0; i < input().size(); ++i) {
  63. if (placeholders[i]->is_host_value_shape_input())
  64. continue;
  65. if (!(cg::is_const_var_shape(input(i)) &&
  66. input(i)->shape().is_scalar())) {
  67. if (non_scalar.valid()) {
  68. non_scalar.invalidate();
  69. break;
  70. }
  71. non_scalar = i;
  72. }
  73. }
  74. if (non_scalar.valid()) {
  75. // exactly one input is non-scalar
  76. m_input_broadcastable[non_scalar.val()] = false;
  77. }
  78. }
  79. add_output(None)->dtype(m_internal_graph->output()->dtype());
  80. add_equivalence_component<ScalarHash<void*>>(internal_graph->output());
  81. for (size_t i = 0, it = m_compiler->get_nr_workspace_outputs(this); i < it;
  82. ++i) {
  83. cg::add_workspace_output(this);
  84. }
  85. // check if there is reduce or dimshuffle opr
  86. cg::DepOprIter{[this](cg::OperatorNodeBase* opr) {
  87. if (opr->same_type<opr::Reduce>()) {
  88. m_feature_bits |= JITFeatureBits::REDUCE;
  89. }
  90. if (opr->same_type<opr::Dimshuffle>()) {
  91. m_feature_bits |= JITFeatureBits::DIMSHUFFLE;
  92. }
  93. }}.add(internal_graph->output());
  94. }
  95. void JITExecutor::add_input_layout_constraint() {
  96. if (m_compiler->property().contain_flag(CPFlag::NEED_INPUT_CONTIG)) {
  97. for (auto i : input()) {
  98. i->add_layout_constraint_contiguous();
  99. }
  100. } else {
  101. for (auto i : input()) {
  102. i->add_layout_constraint_monotone();
  103. }
  104. }
  105. }
  106. void JITExecutor::init_output_mem_plan(bool dynamic) {
  107. Super::init_output_mem_plan(dynamic);
  108. m_args.need_update = true;
  109. }
  110. SymbolVar JITExecutor::make(const InternalGraphPtr& internal_graph,
  111. const VarNodeArray& inputs,
  112. const OperatorNodeConfig& config) {
  113. return internal_graph->output()
  114. ->owner_graph()
  115. ->insert_opr(std::make_unique<JITExecutor>(internal_graph, inputs,
  116. config))
  117. ->output(0);
  118. }
  119. void JITExecutor::init_output_static_infer_desc() {
  120. using namespace cg::static_infer;
  121. auto&& mgr = owner_graph()->static_infer_manager();
  122. mgr.register_shape_infer(
  123. output(0),
  124. ShapeInferDesc::make_identity(m_internal_graph->shape_infer()));
  125. m_compiler->init_workspace_size_infer(this);
  126. if (m_internal_graph->value_infer()) {
  127. mgr.register_value_infer(
  128. output(0),
  129. ValueInferDesc::make_identity(m_internal_graph->value_infer()));
  130. }
  131. }
  132. void JITExecutor::scn_do_execute() {
  133. if (m_executable == nullptr || m_args.need_update) {
  134. m_executable = m_compiler->compile(this);
  135. }
  136. m_executable->execute(this);
  137. }
  138. //! change the inputs which depend on dimshuffle opr, make sure dimshuffles
  139. //! can be ignored
  140. void JITExecutor::do_dimshuffle() {
  141. auto get_dimshuffled_layout = [](const TensorLayout& ily, int32_t* pattern,
  142. size_t pattern_len) {
  143. TensorLayout oly{ily.dtype};
  144. oly.ndim = pattern_len;
  145. bool input_used[TensorLayout::MAX_NDIM] = {0};
  146. for (uint32_t idx = 0; idx < pattern_len; ++idx) {
  147. auto i = pattern[idx];
  148. if (i < 0) {
  149. oly.shape[idx] = 1;
  150. oly.stride[idx] = 1;
  151. } else {
  152. input_used[i] = true;
  153. oly.shape[idx] = ily.shape[i];
  154. oly.stride[idx] = ily.stride[i];
  155. }
  156. }
  157. for (size_t i = 0; i < ily.ndim; ++i) {
  158. mgb_assert(input_used[i] || ily.shape[i] == 1,
  159. "non-1 dim discarded in Dimshuffle: ishp=%s dim=%zd",
  160. static_cast<const TensorShape&>(ily).to_string().c_str(),
  161. i);
  162. }
  163. return oly;
  164. };
  165. // DFS to make sure traverse the dimshuffles in one branch
  166. std::unordered_set<VarNode*> visited;
  167. std::vector<OperatorNodeBase*> stack(0);
  168. std::vector<uint8_t> idx(0); // input index
  169. stack.push_back(m_internal_graph->output()->owner_opr());
  170. idx.push_back(0);
  171. while (!stack.empty()) {
  172. if (idx.back() < stack.back()->input().size() &&
  173. !visited.count(stack.back()->input(idx.back()))) {
  174. visited.insert(stack.back()->input(idx.back()));
  175. stack.push_back(stack.back()->input(idx.back())->owner_opr());
  176. if (stack.back()->same_type<jit::JITPlaceholder>()) {
  177. auto jitph = gopt::try_cast_as_op<JITPlaceholder>(stack.back());
  178. size_t input_id = jitph->input_id();
  179. auto&& input = m_args.inputs[input_id];
  180. for (int i = stack.size() - 1; i >= 0; --i) {
  181. if (stack[i]->same_type<opr::Dimshuffle>()) {
  182. auto param =
  183. stack[i]->cast_final_safe<opr::Dimshuffle>()
  184. .param();
  185. mgb_assert(input.layout.ndim == param.ndim,
  186. "input ndim mismatch for Dimshuffle: "
  187. "expect=%u "
  188. "actual=%zu",
  189. param.ndim, input.layout.ndim);
  190. auto dimshuffled_layout = get_dimshuffled_layout(
  191. input.layout, param.pattern, param.pattern_len);
  192. input.layout = dimshuffled_layout;
  193. }
  194. }
  195. stack.pop_back();
  196. ++idx.back();
  197. } else {
  198. idx.push_back(0);
  199. }
  200. } else {
  201. stack.pop_back();
  202. idx.pop_back();
  203. if (!stack.empty())
  204. ++idx.back();
  205. }
  206. }
  207. }
  208. void JITExecutor::update_args() {
  209. m_args.outputs.clear();
  210. for (auto out : output()) {
  211. m_args.outputs.push_back({out, out->layout(), -1});
  212. }
  213. m_args.inputs.resize(input().size());
  214. auto is_host_value_shape_input = [this](size_t idx) {
  215. return m_internal_graph->placeholders()
  216. .at(idx)
  217. ->is_host_value_shape_input();
  218. };
  219. for (size_t i = 0; i < input().size(); i++) {
  220. auto&& dst_data = m_args.inputs[i];
  221. dst_data.from = input(i);
  222. dst_data.idx = i;
  223. if (is_host_value_shape_input(i)) {
  224. auto&& mgr = owner_graph()->static_infer_manager();
  225. auto&& shpval_inp_val = &mgr.infer_value(input(i));
  226. cg::copy_tensor_value_to_shape(dst_data.layout, *shpval_inp_val);
  227. dst_data.layout.dtype = {};
  228. for (size_t i = 0; i < dst_data.layout.ndim; ++i) {
  229. dst_data.layout.stride[i] = 0;
  230. }
  231. } else {
  232. dst_data.layout = input(i)->layout();
  233. }
  234. }
  235. //! dimshuffle opr need to change the input.
  236. do_dimshuffle();
  237. if (m_compiler->property().contain_flag(CPFlag::NEED_INPUT_COLLAPSE)) {
  238. // collective collapse datum layout, try to reduce the output ndim
  239. opr::Elemwise::TensorLayoutPtrArray inp_layouts;
  240. inp_layouts.reserve(m_args.inputs.size());
  241. for (size_t i = 0; i < m_args.inputs.size(); i++) {
  242. if (!is_host_value_shape_input(i)) {
  243. inp_layouts.push_back(&m_args.inputs[i].layout);
  244. }
  245. }
  246. opr::Elemwise::broadcast_collective_collapse(inp_layouts,
  247. &m_args.outputs[0].layout);
  248. }
  249. // compute and update hash
  250. XXHash hstate;
  251. // update layout info
  252. auto prop = m_compiler->property();
  253. if (prop.contain_flag(CPFlag::BIND_NDIM | CPFlag::BIND_SHAPE)) {
  254. mgb_assert(prop.contain_flag(CPFlag::BIND_NDIM),
  255. "BIND_NDIM must be set if bind_shape is set");
  256. std::vector<size_t> buf;
  257. buf.reserve(1024);
  258. buf.push_back(m_args.inputs.size());
  259. for (auto&& i : m_args.inputs) {
  260. buf.push_back(i.layout.ndim);
  261. if (prop.contain_flag(CPFlag::BIND_SHAPE)) {
  262. for (size_t j = 0; j < i.layout.ndim; ++j) {
  263. buf.push_back(i.layout[j]);
  264. }
  265. }
  266. }
  267. hstate.update(buf.data(), sizeof(buf[0]) * buf.size());
  268. }
  269. m_args.hash = hstate.digest();
  270. // update version number
  271. static std::atomic_uint_fast64_t global_version;
  272. m_args.version = global_version.fetch_add(1);
  273. m_args.need_update = false;
  274. }
  275. const JITExecutor::Args& JITExecutor::args() const {
  276. if (m_args.need_update) {
  277. const_cast<JITExecutor*>(this)->update_args();
  278. }
  279. return m_args;
  280. }
  281. bool JITExecutor::Args::operator==(const Args& rhs) const {
  282. auto&& lhs = *this;
  283. mgb_assert(!lhs.need_update && !rhs.need_update);
  284. if (lhs.hash != rhs.hash) {
  285. return false;
  286. }
  287. if (lhs.version == rhs.version) {
  288. return true;
  289. }
  290. if (lhs.outputs.size() != rhs.outputs.size())
  291. return false;
  292. if (lhs.inputs.size() != rhs.inputs.size())
  293. return false;
  294. auto prop = owner->m_compiler->property();
  295. if (prop.contain_flag(CPFlag::BIND_NDIM | CPFlag::BIND_SHAPE)) {
  296. bool (*chk_layout)(const TensorLayout&, const TensorLayout&);
  297. if (prop.contain_flag(CPFlag::BIND_SHAPE)) {
  298. chk_layout = [](const TensorLayout& lhs, const TensorLayout& rhs) {
  299. return lhs.eq_shape(rhs);
  300. };
  301. } else {
  302. chk_layout = [](const TensorLayout& lhs, const TensorLayout& rhs) {
  303. return lhs.ndim == rhs.ndim;
  304. };
  305. }
  306. for (size_t i = 0; i < lhs.inputs.size(); i++) {
  307. if (!chk_layout(lhs.inputs[i].layout, rhs.inputs[i].layout))
  308. return false;
  309. }
  310. for (size_t i = 0; i < lhs.outputs.size(); i++) {
  311. if (!chk_layout(lhs.outputs[i].layout, rhs.outputs[i].layout))
  312. return false;
  313. }
  314. }
  315. // elect a common version so next check can be fast
  316. lhs.version = rhs.version = std::min(lhs.version, rhs.version);
  317. return true;
  318. }
  319. JITExecutor::NodeProp* JITExecutor::do_make_node_prop() const {
  320. auto ret = Super::do_make_node_prop();
  321. using DepType = NodeProp::DepType;
  322. SmallVector<DepType> dt(input().size());
  323. auto&& placeholders = internal_graph().placeholders();
  324. for (size_t i = 0; i < dt.size(); ++i) {
  325. dt[i] = placeholders[i]->is_host_value_shape_input()
  326. ? DepType::HOST_VALUE
  327. : DepType::DEV_VALUE;
  328. }
  329. ret->reset_dep_type(input(), dt);
  330. return ret;
  331. }
  332. megdnn::TensorShape JITExecutor::broadcasted_input_shape() const {
  333. megdnn::TensorShapeArray inp_shps;
  334. megdnn::TensorShape brdcast_shp;
  335. auto placeholders = m_internal_graph->placeholders();
  336. for (auto ph : placeholders) {
  337. if (!ph->is_host_value_shape_input()) {
  338. inp_shps.push_back(input(ph->input_id())->shape());
  339. }
  340. }
  341. megdnn::Elemwise::deduce_shape(inp_shps, brdcast_shp);
  342. return brdcast_shp;
  343. }
  344. #if MGB_ENABLE_GRAD
  345. MGB_IMPL_OPR_GRAD(JITExecutor) {
  346. VarNodeArray grad_inputs;
  347. for (auto input : opr.input())
  348. grad_inputs.push_back(input);
  349. mgb_assert(out_grad[0]);
  350. grad_inputs.push_back(opr.output(0));
  351. grad_inputs.push_back(out_grad[0]);
  352. auto fwd_igraph_ptr = opr.internal_graph_ptr();
  353. auto output_ph = JITPlaceholder::make(
  354. fwd_igraph_ptr->output(), fwd_igraph_ptr->placeholders().size());
  355. auto og_ph = JITPlaceholder::make(
  356. out_grad[0], fwd_igraph_ptr->placeholders().size() + 1);
  357. auto loss = opr::VirtualLoss::make({fwd_igraph_ptr->output()}, {og_ph});
  358. auto gx = cg::grad(loss, fwd_igraph_ptr->placeholders()[wrt_idx]->output(0),
  359. false, false);
  360. if (!gx.node()) {
  361. return nullptr;
  362. }
  363. if (gx.node()->owner_opr()->same_type<opr::InvalidGrad>()) {
  364. return opr::InvalidGrad::make(opr, wrt_idx);
  365. }
  366. if (opr.compiler()->property().feature_bits & JITFeatureBits::REDUCE) {
  367. // expand the gradient graph into the original graph to handle bcast
  368. // oprs
  369. ThinHashMap<VarNode*, VarNode*> old2new;
  370. VarNodeArray new_inp;
  371. auto on_opr = [&old2new, &grad_inputs,
  372. &new_inp](cg::OperatorNodeBase* opr) {
  373. if (auto ph = gopt::try_cast_as_op<JITPlaceholder>(opr)) {
  374. old2new[opr->output(0)] = grad_inputs.at(ph->input_id());
  375. return;
  376. }
  377. if (auto imm = gopt::try_cast_as_op<opr::ImmutableTensor>(opr)) {
  378. HostTensorND hval{grad_inputs[0]->comp_node()};
  379. hval.copy_from(imm->value()).sync();
  380. old2new[opr->output(0)] =
  381. opr::ImmutableTensor::make(*opr->owner_graph(), hval)
  382. .node();
  383. return;
  384. }
  385. new_inp.clear();
  386. for (auto inp : opr->input()) {
  387. new_inp.push_back(old2new.at(inp));
  388. }
  389. auto new_opr = serialization::copy_opr_shallow(*opr, new_inp);
  390. old2new[opr->output(0)] = new_opr->output(0);
  391. };
  392. cg::DepOprIter{on_opr}.add(gx.node());
  393. return old2new.at(gx.node());
  394. } else {
  395. PlaceholderArray placeholders = fwd_igraph_ptr->placeholders();
  396. for (SymbolVar i : {output_ph, og_ph}) {
  397. placeholders.push_back(
  398. &i.node()->owner_opr()->cast_final_safe<JITPlaceholder>());
  399. }
  400. for (size_t i = 0; i < placeholders.size(); ++i) {
  401. if (gx.node() == placeholders[i]->output(0)) {
  402. return grad_inputs[i];
  403. }
  404. }
  405. auto grad_ig = std::make_shared<InternalGraph>(
  406. gx.node(), fwd_igraph_ptr->shape_infer(), nullptr,
  407. std::move(placeholders));
  408. auto grad_jit = JITExecutor::make(grad_ig, grad_inputs);
  409. if (opr.input_broadcastable()[wrt_idx]) {
  410. grad_jit = opr::reduce_sum(
  411. grad_jit, opr::GetVarShape::make(opr.input(wrt_idx)));
  412. }
  413. return grad_jit.node();
  414. }
  415. }
  416. #endif // MGB_ENABLE_GRAD
  417. #endif // MGB_JIT
  418. // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台