Benchmark2.C 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright (C) 2011 Emweb bvba, Kessel-Lo, Belgium.
  3. *
  4. * See the LICENSE file for terms of use.
  5. */
  6. #include <boost/test/unit_test.hpp>
  7. #include <Wt/Dbo/Dbo>
  8. #include <Wt/WDateTime>
  9. #include <Wt/Dbo/WtSqlTraits>
  10. #include <Wt/Dbo/QueryModel>
  11. #include "DboFixture.h"
  12. namespace dbo = Wt::Dbo;
  13. namespace {
  14. const unsigned total_added_objects = 20000;
  15. const unsigned start_total_objects = 500;
  16. const unsigned benchmark_time_limit = 10; //seconds (for a benchmark round)
  17. }
  18. namespace Perf2 {
  19. class Post {
  20. public:
  21. int counter;
  22. template<class Action>
  23. void persist(Action& a)
  24. {
  25. dbo::field(a, counter, "counter");
  26. }
  27. };
  28. }
  29. struct DboBenchmark2Fixture : DboFixtureBase
  30. {
  31. DboBenchmark2Fixture() :
  32. DboFixtureBase(false)
  33. {
  34. session_->mapClass<Perf2::Post>("post");
  35. try {
  36. session_->dropTables();
  37. } catch (...) {
  38. }
  39. session_->createTables();
  40. }
  41. };
  42. long benchmarkQuery(
  43. dbo::Session& session,
  44. const std::string& query_sql,
  45. int total_objects,
  46. int batch_size,
  47. long expected,
  48. bool force_select_count)
  49. {
  50. dbo::Transaction t(session);
  51. /*
  52. * clear Dbo Session cache
  53. */
  54. session.rereadAll();
  55. boost::posix_time::ptime start
  56. = boost::posix_time::microsec_clock::local_time();
  57. typedef boost::tuple<dbo::ptr<Perf2::Post>, double> PostWithSum;
  58. dbo::Query<PostWithSum> query = session.query<PostWithSum>(query_sql);
  59. dbo::QueryModel< PostWithSum > model;
  60. model.setQuery(query);
  61. model.addAllFieldsAsColumns();
  62. std::cerr << "QueryModel " << total_objects
  63. << " rows in batches of " << batch_size
  64. << (force_select_count ? " (force count): " : " : ");
  65. long total_sum = 0L;
  66. BOOST_REQUIRE(model.columnCount() == 4); // id, version, counter, sum_total
  67. /*
  68. * FOR TESTING ONLY: Force select count by setting batch size to zero
  69. */
  70. if (force_select_count)
  71. model.setBatchSize(0);
  72. else
  73. model.setBatchSize(batch_size);
  74. BOOST_REQUIRE(model.rowCount() == total_objects);
  75. if (force_select_count)
  76. model.setBatchSize(batch_size);
  77. long sum;
  78. dbo::ptr<Perf2::Post> post;
  79. for (int i = 0; i < total_objects; i++) {
  80. boost::tie(post, sum) = model.resultRow(i);
  81. total_sum += sum;
  82. }
  83. boost::posix_time::ptime
  84. end = boost::posix_time::microsec_clock::local_time();
  85. boost::posix_time::time_duration d = end - start;
  86. std::cerr << (double)d.total_microseconds() / total_objects
  87. << " microseconds per object" << std::endl;
  88. BOOST_REQUIRE(total_sum == expected);
  89. return d.total_seconds();
  90. }
  91. BOOST_AUTO_TEST_CASE( performance_test2 )
  92. {
  93. DboBenchmark2Fixture f;
  94. dbo::Session &session = *(f.session_);
  95. /*
  96. * This query is expensive to count() because of the WHERE clause,
  97. * at least with Postgres and Sqlite3. SELECT statements that use
  98. * WITH clauses or WINDOW functions can also be expensive to count.
  99. */
  100. std::string query_post_with_sum =
  101. "SELECT \"p1\", (SELECT sum(\"p2\".\"counter\") * (avg(\"p2\".\"counter\")) \"sum_total\" "
  102. "FROM \"post\" \"p2\" WHERE \"p2\".\"id\" <= \"p1\".\"id\") "
  103. "FROM \"post\" \"p1\" "
  104. "WHERE (SELECT sum(\"p2\".\"counter\") * (avg(\"p2\".\"counter\")) \"sum_total\" "
  105. "FROM \"post\" \"p2\" WHERE \"p2\".\"id\" <= \"p1\".\"id\") > 0";
  106. long time_required = 0L;
  107. long expected;
  108. int current_objects = 0;
  109. for (unsigned i = start_total_objects;
  110. (i <= total_added_objects) && ((time_required * 6) < benchmark_time_limit); i *= 2) {
  111. expected = i * (i + 1) / 2;
  112. int total_objects = i;
  113. dbo::Transaction t(session);
  114. std::cerr << "Loading " << total_objects << " objects in database."
  115. << std::endl;
  116. for (int i = 0; i < total_objects - current_objects; ++i) {
  117. Perf2::Post *p = new Perf2::Post();
  118. p->counter = 1;
  119. session.add(p);
  120. }
  121. t.commit();
  122. current_objects = total_objects;
  123. /*
  124. * Three cases:
  125. *
  126. * 1. Batch size is greater than total rows in table. One SELECT.
  127. * 2. Batch size is equal to number of rows in table. All rows are fetched in
  128. * single SELECT, but a second SELECT for count is still required because in
  129. * general case, Dbo is not sure whether or not more rows exist.
  130. * 3. Batch size is less than number of rows. A SELECT for count is required.
  131. */
  132. time_required = benchmarkQuery(
  133. session, query_post_with_sum, total_objects, total_objects + 1, expected, 0);
  134. time_required = benchmarkQuery(
  135. session, query_post_with_sum, total_objects, total_objects + 0, expected, 0);
  136. time_required = benchmarkQuery(
  137. session, query_post_with_sum, total_objects, total_objects - 1, expected, 0);
  138. std::cerr << std::endl;
  139. /*
  140. * Repeat above tests, but force a separate select for counting.
  141. */
  142. time_required = benchmarkQuery(
  143. session, query_post_with_sum, total_objects, total_objects + 1, expected, 1);
  144. time_required = benchmarkQuery(
  145. session, query_post_with_sum, total_objects, total_objects + 0, expected, 1);
  146. time_required = benchmarkQuery(
  147. session, query_post_with_sum, total_objects, total_objects - 1, expected, 1);
  148. std::cerr << std::endl;
  149. }
  150. }