fake_resolution.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <resolver/bench/fake_resolution.h>
  15. #include <resolver/bench/dummy_work.h>
  16. #include <asiolink/interval_timer.h>
  17. #include <boost/bind.hpp>
  18. #include <boost/foreach.hpp>
  19. #include <algorithm>
  20. #include <cstdlib>
  21. namespace isc {
  22. namespace resolver {
  23. namespace bench {
  24. // Parameters of the generated queries.
  25. // How much work is each operation?
  26. const size_t parse_size = 100000;
  27. const size_t render_size = 100000;
  28. const size_t send_size = 1000;
  29. const size_t cache_read_size = 10000;
  30. const size_t cache_write_size = 10000;
  31. // How large a change is to terminate in this iteration (either by getting
  32. // the complete answer, or by finding it in the cache). With 0.5, half the
  33. // queries are found in the cache directly. Half of the rest needs just one
  34. // upstream query. Etc.
  35. const float chance_complete = 0.5;
  36. // Number of milliseconds an upstream query can take. It picks a random number
  37. // in between.
  38. const size_t upstream_time_min = 2;
  39. const size_t upstream_time_max = 50;
  40. FakeQuery::FakeQuery(FakeInterface& interface) :
  41. interface_(&interface),
  42. outstanding_(false)
  43. {
  44. // Schedule what tasks are needed.
  45. // First, parse the query
  46. steps_.push_back(Step(Compute, parse_size));
  47. // Look into the cache if it is there
  48. steps_.push_back(Step(CacheRead, cache_read_size));
  49. while ((1.0 * random()) / RAND_MAX > chance_complete) {
  50. // Needs another step of recursion. Render the upstream query.
  51. steps_.push_back(Step(Compute, render_size));
  52. // Send it and wait for the answer.
  53. steps_.push_back(Step(Upstream, upstream_time_min +
  54. (random() *
  55. (upstream_time_max - upstream_time_min) /
  56. RAND_MAX)));
  57. // After it comes, parse the answer and store it in the cache.
  58. steps_.push_back(Step(Compute, parse_size));
  59. steps_.push_back(Step(CacheWrite, cache_write_size));
  60. }
  61. // Last, render the answer and send it.
  62. steps_.push_back(Step(Compute, render_size));
  63. steps_.push_back(Step(Send, send_size));
  64. // Reverse it, so we can pop_back the tasks as we work on them.
  65. std::reverse(steps_.begin(), steps_.end());
  66. }
  67. void
  68. FakeQuery::performTask(const StepCallback& callback) {
  69. // nextTask also does all the sanity checking we need.
  70. if (nextTask() == Upstream) {
  71. outstanding_ = true;
  72. interface_->scheduleUpstreamAnswer(this, callback,
  73. steps_.back().second);
  74. steps_.pop_back();
  75. } else {
  76. for (size_t i = 0; i < steps_.back().second; ++i) {
  77. dummy_work();
  78. }
  79. steps_.pop_back();
  80. callback();
  81. }
  82. }
  83. FakeInterface::FakeInterface(size_t query_count) :
  84. queries_(query_count)
  85. {
  86. BOOST_FOREACH(FakeQueryPtr& query, queries_) {
  87. query = FakeQueryPtr(new FakeQuery(*this));
  88. }
  89. }
  90. void
  91. FakeInterface::processEvents() {
  92. service_.run_one();
  93. }
  94. namespace {
  95. void
  96. processDone(bool* flag) {
  97. *flag = true;
  98. }
  99. }
  100. FakeQueryPtr
  101. FakeInterface::receiveQuery() {
  102. // Handle all the events that are already scheduled.
  103. // As processEvents blocks until an event happens and we want to terminate
  104. // if there are no events, we do a small trick. We post an event to the end
  105. // of the queue and work until it is found. This should process all the
  106. // events that were there already.
  107. bool processed = false;
  108. service_.post(boost::bind(&processDone, &processed));
  109. while (!processed) {
  110. processEvents();
  111. }
  112. // Now, look if there are more queries to return.
  113. if (queries_.empty()) {
  114. return (FakeQueryPtr());
  115. } else {
  116. // Take from the back. The order doesn't matter and it's faster from
  117. // there.
  118. FakeQueryPtr result(queries_.back());
  119. queries_.pop_back();
  120. return (result);
  121. }
  122. }
  123. class FakeInterface::UpstreamQuery {
  124. public:
  125. UpstreamQuery(FakeQuery* query, const FakeQuery::StepCallback& callback,
  126. const boost::shared_ptr<asiolink::IntervalTimer> timer) :
  127. query_(query),
  128. callback_(callback),
  129. timer_(timer)
  130. {}
  131. void trigger() {
  132. query_->outstanding_ = false;
  133. callback_();
  134. // We are not needed any more.
  135. delete this;
  136. }
  137. private:
  138. FakeQuery* const query_;
  139. const FakeQuery::StepCallback callback_;
  140. // Just to hold it alive before the callback is called.
  141. const boost::shared_ptr<asiolink::IntervalTimer> timer_;
  142. };
  143. void
  144. FakeInterface::scheduleUpstreamAnswer(FakeQuery* query,
  145. const FakeQuery::StepCallback& callback,
  146. size_t msec)
  147. {
  148. const boost::shared_ptr<asiolink::IntervalTimer>
  149. timer(new asiolink::IntervalTimer(service_));
  150. UpstreamQuery* q(new UpstreamQuery(query, callback, timer));
  151. timer->setup(boost::bind(&UpstreamQuery::trigger, q), msec);
  152. }
  153. }
  154. }
  155. }