bench-stencil2.cc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // -*- mode: c++; coding: utf-8 -*-
  2. /// @file bench-stencil2.cc
  3. /// @brief Stencil-as-view.
  4. // (c) Daniel Llorens - 2016-2017
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU Lesser General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. // TODO Bad performance, see also bench-stencil[13].cc.
  10. #include <iostream>
  11. #include <iomanip>
  12. #include <random>
  13. #include "ra/ra.hh"
  14. #include "ra/test.hh"
  15. #include "ra/bench.hh"
  16. using std::cout, std::endl, std::flush, ra::TestRecorder;
  17. using real = double;
  18. int nx = 1000;
  19. int ny = 1000;
  20. int ts = 10;
  21. auto I = ra::iota(nx-2, 1);
  22. auto J = ra::iota(ny-2, 1);
  23. constexpr ra::Small<real, 3, 3> mask = { 0, 1, 0,
  24. 1, -4, 1,
  25. 0, 1, 0 };
  26. #define THEOP template <class A_, class Anext_, class Astencil_> __attribute__((noinline)) \
  27. auto operator()(A_ & A, Anext_ & Anext, Astencil_ & Astencil)
  28. // sensitive to RA_DO_CHECK.
  29. struct f_raw
  30. {
  31. THEOP
  32. {
  33. for (int i=1; i+1<nx; ++i) {
  34. for (int j=1; j+1<ny; ++j) {
  35. Anext(i, j) = -4*A(i, j)
  36. + A(i+1, j) + A(i, j+1)
  37. + A(i-1, j) + A(i, j-1);
  38. }
  39. }
  40. std::swap(A.p, Anext.p);
  41. };
  42. };
  43. // about as fast as f_raw, but no stencil. Insensitive to RA_DO_CHECK.
  44. struct f_slices
  45. {
  46. THEOP
  47. {
  48. Anext(I, J) = -4*A(I, J)
  49. + A(I+1, J) + A(I, J+1)
  50. + A(I-1, J) + A(I, J-1);
  51. std::swap(A.p, Anext.p);
  52. };
  53. };
  54. // with stencil, about as fast as f_raw. Sensitive to RA_DO_CHECK.
  55. struct f_stencil_explicit
  56. {
  57. THEOP
  58. {
  59. Astencil.p = A.data();
  60. Anext(I, J) = map([](auto && A) { return -4*A(1, 1)
  61. + A(2, 1) + A(1, 2)
  62. + A(0, 1) + A(1, 0); },
  63. iter<2>(Astencil));
  64. std::swap(A.p, Anext.p);
  65. };
  66. };
  67. // sum() inside uses run time sizes and 2-dim ply_ravel loop which is slower (2x w/gcc). TODO
  68. struct f_stencil_arrayop
  69. {
  70. THEOP
  71. {
  72. Astencil.p = A.data();
  73. Anext(I, J) = map([](auto && s) { return sum(s*mask); }, iter<2>(Astencil));
  74. std::swap(A.p, Anext.p);
  75. };
  76. };
  77. // allows traversal order to be chosen between all 6 axes in ply_ravel. 30x slower. TODO
  78. struct f_sumprod
  79. {
  80. THEOP
  81. {
  82. Astencil.p = A.data();
  83. Anext(I, J) = 0; // TODO miss notation for sum-of-axes without preparing destination...
  84. Anext(I, J) += map(ra::wrank<2, 2>(ra::times()), Astencil, mask);
  85. std::swap(A.p, Anext.p);
  86. };
  87. };
  88. // variant of the above, much faster (TODO).
  89. struct f_sumprod2
  90. {
  91. THEOP
  92. {
  93. Astencil.p = A.data();
  94. Anext(I, J) = 0;
  95. plyf(map(ra::wrank<0, 2, 2>([](auto && A, auto && B, auto && C) { A += B*C; }), Anext(I, J), Astencil, mask));
  96. std::swap(A.p, Anext.p);
  97. };
  98. };
  99. int main()
  100. {
  101. TestRecorder tr(std::cout);
  102. std::random_device rand;
  103. real value = rand();
  104. auto bench = [&](auto & A, auto & Anext, auto & Astencil, auto && ref, auto && tag, auto && f)
  105. {
  106. auto bv = Benchmark().repeats(ts).runs(3)
  107. .once_f([&](auto && repeat)
  108. {
  109. Anext = 0.;
  110. A = value;
  111. repeat([&]() { f(A, Anext, Astencil); });
  112. });
  113. tr.info(std::setw(5), std::fixed, Benchmark::avg(bv)/A.size()/1e-9, " ns [",
  114. Benchmark::stddev(bv)/A.size()/1e-9 ,"] ", tag)
  115. .skip().test_rel_error(ref, A, 1e-10);
  116. };
  117. ra::Big<real, 2> Aref;
  118. tr.section("static rank");
  119. {
  120. ra::Big<real, 2> A({nx, ny}, 1.);
  121. ra::Big<real, 2> Anext({nx, ny}, 0.);
  122. auto Astencil = stencil(A, 1, 1);
  123. cout << "Astencil " << format_array(Astencil(0, 0, ra::dots<2>), "|", " ") << endl;
  124. #define BENCH(ref, op) bench(A, Anext, Astencil, ref, STRINGIZE(op), op {});
  125. BENCH(A, f_raw);
  126. Aref = ra::Big<real, 2>(A);
  127. BENCH(Aref, f_slices);
  128. BENCH(Aref, f_stencil_explicit);
  129. BENCH(Aref, f_stencil_arrayop);
  130. BENCH(Aref, f_sumprod);
  131. BENCH(Aref, f_sumprod2);
  132. #undef BENCH
  133. }
  134. tr.section("dynamic rank");
  135. {
  136. ra::Big<real> B({nx, ny}, 1.);
  137. ra::Big<real> Bnext({nx, ny}, 0.);
  138. auto Bstencil = stencil(B, 1, 1);
  139. cout << "Bstencil " << format_array(Bstencil(0, 0, ra::dots<2>), "|", " ") << endl;
  140. #define BENCH(ref, op) bench(B, Bnext, Bstencil, ref, STRINGIZE(op), op {});
  141. // BENCH(Aref, f_raw); // TODO very slow
  142. BENCH(Aref, f_slices);
  143. BENCH(Aref, f_stencil_explicit);
  144. BENCH(Aref, f_stencil_arrayop);
  145. #undef BENCH
  146. }
  147. return tr.summary();
  148. }