perf_condition_variable.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // (C) Copyright 2012 Vicente J. Botet Escriba
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See
  4. // accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // This performance test is based on the performance test provided by maxim.yegorushkin
  8. // at https://svn.boost.org/trac/boost/ticket/7422
  9. #define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS
  10. #include <boost/thread/condition_variable.hpp>
  11. #include <boost/thread/mutex.hpp>
  12. #include <boost/chrono/stopwatches/simple_stopwatch.hpp>
  13. #include <condition_variable>
  14. #include <future>
  15. #include <limits>
  16. #include <cstdio>
  17. #include <thread>
  18. #include <mutex>
  19. #include <vector>
  20. ////////////////////////////////////////////////////////////////////////////////////////////////
  21. namespace
  22. {
  23. ////////////////////////////////////////////////////////////////////////////////////////////////
  24. // class Stopwatch
  25. // {
  26. // public:
  27. // typedef long long rep;
  28. //
  29. // static rep now()
  30. // {
  31. // timespec ts;
  32. // if (clock_gettime(CLOCK_MONOTONIC, &ts)) abort();
  33. // return ts.tv_sec * rep(1000000000) + ts.tv_nsec;
  34. // }
  35. //
  36. // Stopwatch() :
  37. // start_(now())
  38. // {
  39. // }
  40. //
  41. // rep elapsed() const
  42. // {
  43. // return now() - start_;
  44. // }
  45. //
  46. // private:
  47. // rep start_;
  48. // };
  49. typedef boost::chrono::simple_stopwatch<> Stopwatch;
  50. ////////////////////////////////////////////////////////////////////////////////////////////////
  51. struct BoostTypes
  52. {
  53. typedef boost::condition_variable condition_variable;
  54. typedef boost::mutex mutex;
  55. typedef boost::mutex::scoped_lock scoped_lock;
  56. };
  57. struct StdTypes
  58. {
  59. typedef std::condition_variable condition_variable;
  60. typedef std::mutex mutex;
  61. typedef std::unique_lock<std::mutex> scoped_lock;
  62. };
  63. template <class Types>
  64. struct SharedData: Types
  65. {
  66. unsigned const iterations;
  67. unsigned counter;
  68. unsigned semaphore;
  69. typename Types::condition_variable cnd;
  70. typename Types::mutex mtx;
  71. Stopwatch::rep producer_time;
  72. SharedData(unsigned iterations, unsigned consumers) :
  73. iterations(iterations), counter(), semaphore(consumers) // Initialize to the number of consumers. (*)
  74. , producer_time()
  75. {
  76. }
  77. };
  78. ////////////////////////////////////////////////////////////////////////////////////////////////
  79. template <class S>
  80. void producer_thread(S* shared_data)
  81. {
  82. Stopwatch sw;
  83. unsigned const consumers = shared_data->semaphore; // (*)
  84. for (unsigned i = shared_data->iterations; i--;)
  85. {
  86. {
  87. typename S::scoped_lock lock(shared_data->mtx);
  88. // Wait till all consumers signal.
  89. while (consumers != shared_data->semaphore)
  90. {
  91. shared_data->cnd.wait(lock);
  92. }
  93. shared_data->semaphore = 0;
  94. // Signal consumers.
  95. ++shared_data->counter;
  96. }
  97. shared_data->cnd.notify_all();
  98. }
  99. shared_data->producer_time = sw.elapsed().count();
  100. }
  101. template <class S>
  102. void consumer_thread(S* shared_data)
  103. {
  104. unsigned counter = 0;
  105. while (counter != shared_data->iterations)
  106. {
  107. {
  108. typename S::scoped_lock lock(shared_data->mtx);
  109. // Wait till the producer signals.
  110. while (counter == shared_data->counter)
  111. {
  112. shared_data->cnd.wait(lock);
  113. }
  114. counter = shared_data->counter;
  115. // Signal the producer.
  116. ++shared_data->semaphore;
  117. }
  118. shared_data->cnd.notify_all();
  119. }
  120. }
  121. ////////////////////////////////////////////////////////////////////////////////////////////////
  122. template <class Types>
  123. Stopwatch::rep benchmark_ping_pong(unsigned consumer_count)
  124. {
  125. typedef SharedData<Types> S;
  126. auto best_producer_time = std::numeric_limits<Stopwatch::rep>::max BOOST_PREVENT_MACRO_SUBSTITUTION ();
  127. std::vector<std::thread> consumers
  128. { consumer_count };
  129. // Run the benchmark 10 times and report the best time.
  130. for (int times = 10; times--;)
  131. {
  132. S shared_data
  133. { 100000, consumer_count };
  134. // Start the consumers.
  135. for (unsigned i = 0; i < consumer_count; ++i)
  136. consumers[i] = std::thread
  137. { consumer_thread<S> , &shared_data };
  138. // Start the producer and wait till it finishes.
  139. std::thread
  140. { producer_thread<S> , &shared_data }.join();
  141. // Wait till consumers finish.
  142. for (unsigned i = 0; i < consumer_count; ++i)
  143. consumers[i].join();
  144. best_producer_time = std::min BOOST_PREVENT_MACRO_SUBSTITUTION (best_producer_time, shared_data.producer_time);
  145. }
  146. return best_producer_time;
  147. }
  148. ////////////////////////////////////////////////////////////////////////////////////////////////
  149. } // namespace
  150. ////////////////////////////////////////////////////////////////////////////////////////////////
  151. // sudo chrt -f 99 /usr/bin/time -f "\n***\ntime: %E\ncontext switches: %c\nwaits: %w" /home/max/otsquant/build/Linux-x86_64-64.g++-release/test/test
  152. /*
  153. Producer-consumer ping-pong tests. It aims to benchmark condition variables with and without
  154. thread cancellation support by comparing the time it took to complete the benchmark.
  155. Condition variable with thread cancellation support is boost::condition_variable from
  156. boost-1.51. Without - std::condition_variable that comes with gcc-4.7.2.
  157. One producer, one to CONSUMER_MAX consumers. The benchmark calls
  158. condition_variable::notify_all() without holding a mutex to maximize contention within this
  159. function. Each benchmark for a number of consumers is run three times and the best time is
  160. picked to get rid of outliers.
  161. The results are reported for each benchmark for a number of consumers. The most important number
  162. is (std - boost) / std * 100. Positive numbers are when boost::condition_variable is faster,
  163. negative it is slower.
  164. */
  165. int main()
  166. {
  167. std::printf("MAIN\n");
  168. enum
  169. {
  170. CONSUMER_MAX = 2
  171. };
  172. struct
  173. {
  174. Stopwatch::rep boost, std;
  175. } best_times[CONSUMER_MAX] = {};
  176. for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
  177. {
  178. auto& b = best_times[i - 1];
  179. std::printf("STD: %d\n", i);
  180. b.std = benchmark_ping_pong<StdTypes> (i);
  181. std::printf("BOOST: %d\n", i);
  182. b.boost = benchmark_ping_pong<BoostTypes> (i);
  183. std::printf("consumers: %4d\n", i);
  184. std::printf("best std producer time: %15.9fsec\n", b.std * 1e-9);
  185. std::printf("best boost producer time: %15.9fsec\n", b.boost * 1e-9);
  186. std::printf("(std - boost) / std: %7.2f%%\n", (b.std - b.boost) * 100. / b.std);
  187. }
  188. printf("\ncsv:\n\n");
  189. printf("consumers,(std-boost)/std,std,boost\n");
  190. for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
  191. {
  192. auto& b = best_times[i - 1];
  193. printf("%d,%f,%lld,%lld\n", i, (b.std - b.boost) * 100. / b.std, b.std, b.boost);
  194. }
  195. return 1;
  196. }