perf_gemm.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. #include "perf_precomp.hpp"
  5. #include <opencv2/dnn/shape_utils.hpp>
  6. #include <numeric>
  7. namespace opencv_test {
  8. struct GemmParam_t {
  9. std::vector<int> a_shape;
  10. std::vector<int> b_shape;
  11. std::vector<int> c_shape;
  12. bool trans_a;
  13. bool trans_b;
  14. GemmParam_t(std::vector<int> a_shape_, std::vector<int> b_shape_, std::vector<int> c_shape_ = {}, bool trans_a_ = false, bool trans_b_ = false)
  15. : a_shape(a_shape_), b_shape(b_shape_), c_shape(c_shape_), trans_a(trans_a_), trans_b(trans_b_) {}
  16. };
  17. // TODO: Dsiable most of the test cases except vision transformers to save time
  18. static const GemmParam_t test_gemm_configs[] = {
  19. // vision transformers cases
  20. { { 768, 768 }, { 768, 768 }, { 768 } },
  21. { { 1024, 1024 }, { 1024, 1024 }, { 1024 } },
  22. { { 50, 768 }, { 768, 2304 } },
  23. { { 197, 768 }, { 768, 2304 } },
  24. { { 50, 1024 }, { 1024, 3072 } },
  25. { { 197, 1024 }, { 1024, 3072 } },
  26. // these cases are commented to save testing time
  27. /*
  28. // square mat
  29. { { 64, 64 }, { 64, 64 } },
  30. { { 128, 128 }, { 128, 128 } },
  31. { { 256, 256 }, { 256, 256 } },
  32. { { 512, 512 }, { 512, 512 } },
  33. { { 1024, 1024 }, { 1024, 1024 } },
  34. { { 4096, 4096 }, { 4096, 4096 } },
  35. // retangular mat
  36. { { 256, 256 }, { 256, 1024 } },
  37. { { 256, 1024 }, { 1024, 256 } },
  38. { { 256, 1024 }, { 1024, 1024 } },
  39. { { 1024, 1024 }, { 1024, 256 } },
  40. { { 1024, 256 }, { 256, 1024 } },
  41. { { 1024, 256 }, { 256, 256 } },
  42. // with C
  43. { { 256, 256 }, { 256, 256 }, { 256 } },
  44. { { 256, 256 }, { 256, 1024 }, { 1024 } },
  45. { { 256, 1024 }, { 1024, 256 }, { 256 } },
  46. { { 256, 1024 }, { 1024, 1024 }, { 1024 } },
  47. { { 1024, 1024 }, { 1024, 256 }, { 256 } },
  48. { { 1024, 256 }, { 256, 1024 }, { 1024 } },
  49. { { 1024, 256 }, { 256, 256 }, { 256 } },
  50. // with C and trans_b
  51. { { 256, 256 }, { 256, 256 }, { 256 } , false, true},
  52. { { 256, 1024 }, { 256, 1024 }, { 256 } , false, true},
  53. { { 256, 1024 }, { 1024, 1024 }, { 1024 } , false, true},
  54. { { 1024, 1024 }, { 1024, 1024 }, { 1024 } , false, true},
  55. { { 1024, 256 }, { 1024, 256 }, { 1024 } , false, true},
  56. { { 1024, 256 }, { 256, 256 }, { 256 } , false, true},
  57. // with C and trans_b and trans_a
  58. { { 256, 256 }, { 256, 256 }, { 256 } , true, true},
  59. { { 1024, 256 }, { 256, 1024 }, { 256 } , true, true},
  60. { { 256, 1024 }, { 1024, 256 }, { 1024 } , true, true},
  61. { { 1024, 1024 }, { 1024, 1024 }, { 1024 } , true, true},
  62. */
  63. };
  64. static const GemmParam_t test_matmul_configs[] = {
  65. // vision transformer cases
  66. { {12, 197, 197}, {12, 197, 64} },
  67. { {12, 197, 64 }, {12, 64, 197} },
  68. { {12, 50, 64}, {12, 64, 50} },
  69. { {12, 50, 50}, {12, 50, 64} },
  70. { {16, 197, 197}, {16, 197, 64} },
  71. { {16, 197, 64 }, {16, 64, 197} },
  72. { {16, 50, 64}, {16, 64, 50} },
  73. { {16, 50, 50}, {16, 50, 64} },
  74. };
  75. struct GemmParamId
  76. {
  77. enum {
  78. GEMM_0 = 0,
  79. GEMM_LAST = sizeof(test_gemm_configs) / sizeof(test_gemm_configs[0])
  80. };
  81. int val_;
  82. GemmParamId(int val = 0) : val_(val) {}
  83. operator int() const { return val_; }
  84. static ::testing::internal::ParamGenerator<GemmParamId> all()
  85. {
  86. enum { NUM = (int)GEMM_LAST };
  87. GemmParamId v_[NUM]; for (int i = 0; i < NUM; ++i) { v_[i] = GemmParamId(i); } // reduce generated code size
  88. return ::testing::ValuesIn(v_, v_ + NUM);
  89. }
  90. };
  91. struct MatMulParamId {
  92. enum {
  93. MATMUL_0 = 0,
  94. MATMUL_LAST = sizeof(test_matmul_configs) / sizeof(test_matmul_configs[0])
  95. };
  96. int val_;
  97. MatMulParamId(int val = 0) : val_(val) {}
  98. operator int() const { return val_; }
  99. static ::testing::internal::ParamGenerator<MatMulParamId> all() {
  100. enum { NUM = (int)MATMUL_LAST };
  101. MatMulParamId v_[NUM]; for (int i = 0; i < NUM; i++) { v_[i] = MatMulParamId(i); }
  102. return ::testing::ValuesIn(v_, v_ + NUM);
  103. }
  104. };
  105. static inline void PrintTo(const GemmParamId& v, std::ostream* os)
  106. {
  107. CV_Assert((int)v >= 0); CV_Assert((int)v < GemmParamId::GEMM_LAST);
  108. const GemmParam_t& p = test_gemm_configs[(int)v];
  109. auto print_shape = [os](const std::vector<int>& shape, const std::string tag) {
  110. if (shape.empty()) {
  111. return ;
  112. }
  113. *os << tag << "=[";
  114. for (size_t i = 0; i < shape.size(); ++i) {
  115. if (i == shape.size() - 1) {
  116. *os << shape[i] << "]";
  117. break;
  118. }
  119. *os << shape[i] << ", ";
  120. }
  121. };
  122. print_shape(p.a_shape, "A");
  123. print_shape(p.b_shape, ", B");
  124. print_shape(p.c_shape, ", C");
  125. *os << ", trans_a=" << p.trans_a << ", trans_b=" << p.trans_b;
  126. }
  127. typedef tuple<GemmParamId, tuple<Backend, Target> > GemmTestParam_t;
  128. typedef TestBaseWithParam<GemmTestParam_t> Gemm;
  129. PERF_TEST_P_(Gemm, gemm)
  130. {
  131. int test_id = (int)get<0>(GetParam());
  132. ASSERT_GE(test_id, 0); ASSERT_LT(test_id, GemmParamId::GEMM_LAST);
  133. const GemmParam_t& params = test_gemm_configs[test_id];
  134. auto a_shape = params.a_shape;
  135. auto b_shape = params.b_shape;
  136. auto c_shape = params.c_shape;
  137. auto trans_a = params.trans_a;
  138. auto trans_b = params.trans_b;
  139. float alpha = 1.f;
  140. float beta = 1.f;
  141. Backend backend_id = get<0>(get<1>(GetParam()));
  142. Target target_id = get<1>(get<1>(GetParam()));
  143. bool have_bias = c_shape.empty() ? false : true;
  144. Mat A(static_cast<int>(a_shape.size()), a_shape.data(), CV_32F);
  145. randu(A, -1.0f, 1.0f);
  146. Mat B(static_cast<int>(b_shape.size()), b_shape.data(), CV_32F);
  147. randu(B, -1.0f, 1.0f);
  148. LayerParams lp;
  149. lp.type = "Gemm";
  150. lp.name = "testLayer";
  151. lp.set("transA", trans_a);
  152. lp.set("transB", trans_b);
  153. lp.set("alpha", alpha);
  154. lp.set("beta", beta);
  155. lp.set("real_ndims_C", static_cast<int>(c_shape.size()));
  156. lp.set("constB", true);
  157. lp.blobs.push_back(B);
  158. if (have_bias) {
  159. Mat C(static_cast<int>(c_shape.size()), c_shape.data(), CV_32F);
  160. randu(C, -1.0f, 1.0f);
  161. lp.set("have_bias", true);
  162. lp.set("constC", true);
  163. lp.blobs.push_back(C);
  164. }
  165. Net net;
  166. net.addLayerToPrev(lp.name, lp.type, lp);
  167. net.setPreferableBackend(backend_id);
  168. net.setPreferableTarget(target_id);
  169. // warmup
  170. {
  171. net.setInput(A);
  172. Mat out = net.forward();
  173. }
  174. TEST_CYCLE()
  175. {
  176. Mat res = net.forward();
  177. }
  178. SANITY_CHECK_NOTHING();
  179. }
  180. PERF_TEST_P_(Gemm, innerproduct)
  181. {
  182. int test_id = (int)get<0>(GetParam());
  183. ASSERT_GE(test_id, 0); ASSERT_LT(test_id, GemmParamId::GEMM_LAST);
  184. const GemmParam_t& params = test_gemm_configs[test_id];
  185. auto a_shape = params.a_shape;
  186. auto b_shape = params.b_shape;
  187. auto c_shape = params.c_shape;
  188. auto trans_a = params.trans_a;
  189. auto trans_b = params.trans_b;
  190. Backend backend_id = get<0>(get<1>(GetParam()));
  191. Target target_id = get<1>(get<1>(GetParam()));
  192. bool have_bias = c_shape.empty() ? false : true;
  193. Mat A(static_cast<int>(a_shape.size()), a_shape.data(), CV_32F);
  194. randu(A, -1.0f, 1.0f);
  195. Mat B(static_cast<int>(b_shape.size()), b_shape.data(), CV_32F);
  196. randu(B, -1.0f, 1.0f);
  197. LayerParams lp;
  198. lp.type = "InnerProduct";
  199. lp.name = "testLayer";
  200. if (trans_a) {
  201. cv::transpose(A, A);
  202. }
  203. if (!trans_b) {
  204. cv::transpose(B, B);
  205. }
  206. lp.blobs.push_back(B);
  207. lp.set("num_output", B.size[0]);
  208. if (have_bias) {
  209. Mat C(static_cast<int>(c_shape.size()), c_shape.data(), CV_32F);
  210. randu(C, -1.0f, 1.0f);
  211. lp.blobs.push_back(C);
  212. lp.set("bias_term", true);
  213. } else {
  214. lp.set("bias_term", false);
  215. }
  216. Net net;
  217. net.addLayerToPrev(lp.name, lp.type, lp);
  218. net.setPreferableBackend(backend_id);
  219. net.setPreferableTarget(target_id);
  220. // warmup
  221. {
  222. std::vector<std::string> input_names(1);
  223. input_names[0] = "A";
  224. net.setInputsNames(input_names);
  225. net.setInput(A, input_names[0]);
  226. Mat out = net.forward();
  227. }
  228. TEST_CYCLE()
  229. {
  230. Mat res = net.forward();
  231. }
  232. SANITY_CHECK_NOTHING();
  233. }
  234. static inline void PrintTo(const MatMulParamId& v, std::ostream* os)
  235. {
  236. CV_Assert((int)v >= 0); CV_Assert((int)v < MatMulParamId::MATMUL_LAST);
  237. const GemmParam_t& p = test_matmul_configs[(int)v];
  238. auto print_shape = [os](const std::vector<int>& shape, const std::string tag) {
  239. if (shape.empty()) {
  240. return ;
  241. }
  242. *os << tag << "=[";
  243. for (size_t i = 0; i < shape.size(); ++i) {
  244. if (i == shape.size() - 1) {
  245. *os << shape[i] << "]";
  246. break;
  247. }
  248. *os << shape[i] << ", ";
  249. }
  250. };
  251. print_shape(p.a_shape, "A");
  252. print_shape(p.b_shape, ", B");
  253. print_shape(p.c_shape, ", C");
  254. *os << ", trans_a=" << p.trans_a << ", trans_b=" << p.trans_b;
  255. }
  256. using MatMulTestParam_t = tuple<MatMulParamId, tuple<Backend, Target>>;
  257. using MatMul = TestBaseWithParam<MatMulTestParam_t>;
  258. PERF_TEST_P_(MatMul, matmul)
  259. {
  260. int test_id = (int)get<0>(GetParam());
  261. ASSERT_GE(test_id, 0); ASSERT_LT(test_id, MatMulParamId::MATMUL_LAST);
  262. const GemmParam_t& params = test_matmul_configs[test_id];
  263. auto a_shape = params.a_shape;
  264. auto b_shape = params.b_shape;
  265. auto trans_a = params.trans_a;
  266. auto trans_b = params.trans_b;
  267. float alpha = 1.f;
  268. float beta = 1.f;
  269. Backend backend_id = get<0>(get<1>(GetParam()));
  270. Target target_id = get<1>(get<1>(GetParam()));
  271. Mat A(a_shape, CV_32F);
  272. randu(A, -1.0f, 1.0f);
  273. Mat B(b_shape, CV_32F);
  274. randu(B, -1.0f, 1.0f);
  275. LayerParams lp;
  276. lp.type = "MatMul";
  277. lp.name = "testLayer";
  278. lp.set("transA", trans_a);
  279. lp.set("transB", trans_b);
  280. lp.set("alpha", alpha);
  281. lp.set("beta", beta);
  282. lp.blobs.push_back(B);
  283. Net net;
  284. net.addLayerToPrev(lp.name, lp.type, lp);
  285. net.setPreferableBackend(backend_id);
  286. net.setPreferableTarget(target_id);
  287. // warmup
  288. {
  289. std::vector<std::string> input_names{"A"};
  290. net.setInputsNames(input_names);
  291. net.setInput(A, input_names[0]);
  292. Mat out = net.forward();
  293. }
  294. TEST_CYCLE()
  295. {
  296. Mat res = net.forward();
  297. }
  298. SANITY_CHECK_NOTHING();
  299. }
  300. PERF_TEST_P_(MatMul, innerproduct)
  301. {
  302. int test_id = (int)get<0>(GetParam());
  303. ASSERT_GE(test_id, 0); ASSERT_LT(test_id, MatMulParamId::MATMUL_LAST);
  304. const GemmParam_t& params = test_matmul_configs[test_id];
  305. auto a_shape = params.a_shape;
  306. auto b_shape = params.b_shape;
  307. Backend backend_id = get<0>(get<1>(GetParam()));
  308. Target target_id = get<1>(get<1>(GetParam()));
  309. Mat A(a_shape, CV_32F);
  310. randu(A, -1.0f, 1.0f);
  311. Mat B(b_shape, CV_32F);
  312. randu(B, -1.0f, 1.0f);
  313. LayerParams lp;
  314. lp.type = "InnerProduct";
  315. lp.name = "testLayer";
  316. lp.set("axis", (int)(a_shape.size() - 1));
  317. lp.set("bias_term", false);
  318. // pre-transpose
  319. std::vector<int> order(b_shape.size());
  320. std::iota(order.begin(), order.end(), 0);
  321. std::swap(order.back(), order[b_shape.size() - 2]);
  322. Mat B_transposed;
  323. transposeND(B, order, B_transposed);
  324. lp.blobs.push_back(B_transposed);
  325. lp.set("num_output", int(B_transposed.total(0, b_shape.size() - 1)));
  326. lp.set("is_matmul", true);
  327. Net net;
  328. net.addLayerToPrev(lp.name, lp.type, lp);
  329. net.setPreferableBackend(backend_id);
  330. net.setPreferableTarget(target_id);
  331. // warmup
  332. {
  333. std::vector<std::string> input_names{"A"};
  334. net.setInputsNames(input_names);
  335. net.setInput(A, input_names[0]);
  336. Mat out = net.forward();
  337. }
  338. TEST_CYCLE()
  339. {
  340. Mat res = net.forward();
  341. }
  342. SANITY_CHECK_NOTHING();
  343. }
  344. INSTANTIATE_TEST_CASE_P(/**/, Gemm, Combine(
  345. GemmParamId::all(),
  346. dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp
  347. ));
  348. INSTANTIATE_TEST_CASE_P(/**/, MatMul, Combine(
  349. MatMulParamId::all(),
  350. dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp
  351. ));
  352. } // namespace