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.

opr_io_dump.cpp 6.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #include "./opr_io_dump_text_out.h"
  2. #include "megbrain/test/helper.h"
  3. #include "megbrain/opr/basic_arith_wrapper.h"
  4. #include "megbrain/opr/io.h"
  5. #include "megbrain/opr/tensor_manip.h"
  6. #include "megbrain/plugin/opr_io_dump.h"
  7. #include "megbrain/utils/debug.h"
  8. #include <fstream>
  9. #include <sstream>
  10. using namespace mgb;
  11. namespace {
  12. using PluginMaker =
  13. thin_function<std::unique_ptr<OprIODumpBase>(ComputingGraph*, int level)>;
  14. using ResultChecker = thin_function<void()>;
  15. void run_test(CompNode cn, const PluginMaker& plugin_maker) {
  16. // use a predefiend seed because we have hard-coded the expected outputs
  17. HostTensorGenerator<> gen{0.f, 1.f, /*seed*/ 23};
  18. std::shared_ptr<HostTensorND> host_x;
  19. auto make_expect = [&host_x]() {
  20. HostTensorND ret{host_x->comp_node(), host_x->dtype()};
  21. auto x = host_x->ptr<float>(), p = ret.resize(host_x->shape()).ptr<float>();
  22. auto shp1 = host_x->shape(1);
  23. for (size_t i = 0, it = host_x->shape().total_nr_elems(); i < it; ++i) {
  24. p[i] = (x[i] >= 0.f ? x[i] : 0.f) * (x[i % shp1] + 2.f);
  25. }
  26. return ret;
  27. };
  28. for (size_t record : {0, 1, 2}) {
  29. host_x = gen({2, 3}, cn);
  30. auto graph = ComputingGraph::make();
  31. graph->options().var_sanity_check_first_run = false;
  32. graph->options().comp_node_seq_record_level = record;
  33. graph->options().graph_opt_level = 0;
  34. auto plug = plugin_maker(graph.get(), record);
  35. // make a non-contiguous value, also introduce some shape dependencies
  36. auto sub_brd = [](SymbolVar x) {
  37. using S = opr::Subtensor;
  38. auto zero = x.make_scalar(0), one = x.make_scalar(1), xshp = x.symshape();
  39. return S::make(x, {S::AxisIndexer::make_interval(0, zero, one, None)})
  40. .broadcast(xshp);
  41. };
  42. // write in primitive oprs to ensure stable opr ordering across
  43. // compilers
  44. auto x = opr::Host2DeviceCopy::make_no_fwd(*graph, host_x),
  45. two = x.make_scalar_dt(2), sub = sub_brd(x) + two, xrelu = opr::relu(x),
  46. y = xrelu * sub;
  47. // set stable names so the test can be used when opr naming is disabled
  48. auto cb_rename = [](cg::OperatorNodeBase* opr) {
  49. opr->name(ssprintf("opr%zu", opr->id()));
  50. for (auto i : opr->output()) {
  51. i->name(ssprintf("var%zu", i->id()));
  52. }
  53. };
  54. cg::DepOprIter{cb_rename}.add(y);
  55. HostTensorND host_y;
  56. auto func = graph->compile({make_callback_copy(y, host_y)});
  57. if (record == 2) {
  58. ComputingGraph::assert_destroy(graph);
  59. }
  60. func->execute();
  61. plug->flush_lazy();
  62. MGB_ASSERT_TENSOR_EQ(make_expect(), host_y);
  63. if (record == 2) {
  64. host_x->copy_from(*gen(host_x->shape(), cn));
  65. } else {
  66. // change ptr
  67. *host_x = *gen(host_x->shape(), cn);
  68. }
  69. func->execute();
  70. MGB_ASSERT_TENSOR_EQ(make_expect(), host_y);
  71. for (int i = 0; i < 2; ++i) {
  72. host_x->copy_from(*gen(host_x->shape(), cn));
  73. func->execute();
  74. MGB_ASSERT_TENSOR_EQ(make_expect(), host_y);
  75. }
  76. if (record != 2) {
  77. // change shape
  78. *host_x = *gen({5, 4}, cn);
  79. if (record == 1) {
  80. ASSERT_THROW(func->execute(), MegBrainError);
  81. } else {
  82. func->execute();
  83. MGB_ASSERT_TENSOR_EQ(make_expect(), host_y);
  84. }
  85. }
  86. }
  87. }
  88. void run_test(const PluginMaker& plugin_maker, const ResultChecker& result_checker) {
  89. for (size_t i = 1; i < CompNode::NR_DEVICE_TYPE; ++i) {
  90. auto type = static_cast<CompNode::DeviceType>(i);
  91. if (!check_device_type_avaiable(type))
  92. continue;
  93. if (CompNode::get_device_count(type)) {
  94. auto cn = CompNode::load({type, -1, 0});
  95. if (cn.contain_flag(CompNode::Flag::SUPPORT_RECORDER)) {
  96. run_test(cn, plugin_maker);
  97. ASSERT_FALSE(::testing::Test::HasFailure())
  98. << "failed for comp node " << cn.to_string();
  99. result_checker();
  100. ASSERT_FALSE(::testing::Test::HasFailure())
  101. << "failed for comp node " << cn.to_string();
  102. }
  103. }
  104. }
  105. }
  106. std::vector<std::string> getlines(std::istream& inp, size_t skip_head = 0) {
  107. std::vector<std::string> ret;
  108. for (std::string line; std::getline(inp, line);) {
  109. if (skip_head) {
  110. --skip_head;
  111. } else {
  112. ret.emplace_back(std::move(line));
  113. }
  114. }
  115. return ret;
  116. }
  117. } // anonymous namespace
  118. #if MGB_VERBOSE_TYPEINFO_NAME
  119. TEST(TestOprIODump, Text) {
  120. auto fname_base = output_file("test_opr_iodump");
  121. std::array<std::string, 3> fnames;
  122. auto make_plugin = [&](ComputingGraph* graph, int level) {
  123. fnames.at(level) = ssprintf("%s-%d.txt", fname_base.c_str(), level);
  124. auto ret = std::make_unique<TextOprIODump>(graph, fnames[level].c_str());
  125. ret->print_addr(false);
  126. return ret;
  127. };
  128. auto check_result = [&]() {
  129. for (int level = 0; level < 3; ++level) {
  130. std::ifstream inp_get{fnames[level]};
  131. std::istringstream inp_expect{EXPECTED_TEXT_OUT_REC[level]};
  132. auto lines_get = getlines(inp_get), lines_expect = getlines(inp_expect, 1);
  133. ASSERT_EQ(lines_expect.size(), lines_get.size());
  134. for (size_t i = 0; i < lines_expect.size(); ++i) {
  135. ASSERT_EQ(lines_expect[i], lines_get[i]) << "fail on line " << i;
  136. }
  137. }
  138. for (auto&& i : fnames) {
  139. // clear the content to test if next run does not produce any output
  140. debug::write_to_file(i.c_str(), "Lorem ipsum");
  141. }
  142. };
  143. run_test(make_plugin, check_result);
  144. }
  145. #endif
  146. TEST(TestOprIODump, StdErr) {
  147. MGB_MARK_USED_VAR(EXPECTED_TEXT_OUT_REC);
  148. HostTensorGenerator<> gen;
  149. auto host_x = gen({5});
  150. auto host_y = gen({5});
  151. auto graph = ComputingGraph::make();
  152. std::shared_ptr<FILE> sp(stdout, [](FILE*) {});
  153. auto plugin = std::make_unique<TextOprIODump>(graph.get(), sp);
  154. auto x = opr::Host2DeviceCopy::make(*graph, host_x);
  155. auto y = opr::Host2DeviceCopy::make(*graph, host_y);
  156. auto z = x + y;
  157. HostTensorND host_z;
  158. auto func = graph->compile({make_callback_copy(z, host_z)});
  159. func->execute();
  160. }
  161. TEST(TestOprIODump, Binary) {
  162. auto fname = output_file("");
  163. auto make_plugin = [&](ComputingGraph* graph, int level) {
  164. return std::make_unique<BinaryOprIODump>(graph, fname);
  165. };
  166. run_test(make_plugin, []() {});
  167. }
  168. // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}