• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 #define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS
11 
12 #include <boost/thread/condition_variable.hpp>
13 #include <boost/thread/mutex.hpp>
14 #include <boost/chrono/stopwatches/simple_stopwatch.hpp>
15 
16 #include <condition_variable>
17 #include <future>
18 #include <limits>
19 #include <cstdio>
20 #include <thread>
21 #include <mutex>
22 #include <vector>
23 
24 ////////////////////////////////////////////////////////////////////////////////////////////////
25 
26 namespace
27 {
28   ////////////////////////////////////////////////////////////////////////////////////////////////
29 
30 //  class Stopwatch
31 //  {
32 //  public:
33 //    typedef long long rep;
34 //
35 //    static rep now()
36 //    {
37 //      timespec ts;
38 //      if (clock_gettime(CLOCK_MONOTONIC, &ts)) abort();
39 //      return ts.tv_sec * rep(1000000000) + ts.tv_nsec;
40 //    }
41 //
42 //    Stopwatch() :
43 //      start_(now())
44 //    {
45 //    }
46 //
47 //    rep elapsed() const
48 //    {
49 //      return now() - start_;
50 //    }
51 //
52 //  private:
53 //    rep start_;
54 //  };
55 
56   typedef boost::chrono::simple_stopwatch<> Stopwatch;
57   ////////////////////////////////////////////////////////////////////////////////////////////////
58 
59   struct BoostTypes
60   {
61     typedef boost::condition_variable condition_variable;
62     typedef boost::mutex mutex;
63     typedef boost::mutex::scoped_lock scoped_lock;
64   };
65 
66   struct StdTypes
67   {
68     typedef std::condition_variable condition_variable;
69     typedef std::mutex mutex;
70     typedef std::unique_lock<std::mutex> scoped_lock;
71   };
72 
73   template <class Types>
74   struct SharedData: Types
75   {
76     unsigned const iterations;
77     unsigned counter;
78     unsigned semaphore;
79     typename Types::condition_variable cnd;
80     typename Types::mutex mtx;
81     Stopwatch::rep producer_time;
82 
SharedData__anon62ba191c0111::SharedData83     SharedData(unsigned iterations, unsigned consumers) :
84       iterations(iterations), counter(), semaphore(consumers) // Initialize to the number of consumers. (*)
85           , producer_time()
86     {
87     }
88   };
89 
90 
91   ////////////////////////////////////////////////////////////////////////////////////////////////
92 
93   template <class S>
producer_thread(S * shared_data)94   void producer_thread(S* shared_data)
95   {
96     Stopwatch sw;
97 
98     unsigned const consumers = shared_data->semaphore; // (*)
99     for (unsigned i = shared_data->iterations; i--;)
100     {
101       {
102         typename S::scoped_lock lock(shared_data->mtx);
103         // Wait till all consumers signal.
104         while (consumers != shared_data->semaphore)
105         {
106           shared_data->cnd.wait(lock);
107         }
108         shared_data->semaphore = 0;
109         // Signal consumers.
110         ++shared_data->counter;
111       }
112       shared_data->cnd.notify_all();
113     }
114 
115     shared_data->producer_time = sw.elapsed().count();
116   }
117 
118   template <class S>
consumer_thread(S * shared_data)119   void consumer_thread(S* shared_data)
120   {
121     unsigned counter = 0;
122     while (counter != shared_data->iterations)
123     {
124       {
125         typename S::scoped_lock lock(shared_data->mtx);
126         // Wait till the producer signals.
127         while (counter == shared_data->counter)
128         {
129           shared_data->cnd.wait(lock);
130         }
131         counter = shared_data->counter;
132         // Signal the producer.
133         ++shared_data->semaphore;
134       }
135       shared_data->cnd.notify_all();
136     }
137   }
138 
139   ////////////////////////////////////////////////////////////////////////////////////////////////
140 
141   template <class Types>
benchmark_ping_pong(unsigned consumer_count)142   Stopwatch::rep benchmark_ping_pong(unsigned consumer_count)
143   {
144     typedef SharedData<Types> S;
145 
146     auto best_producer_time = std::numeric_limits<Stopwatch::rep>::max BOOST_PREVENT_MACRO_SUBSTITUTION ();
147 
148     std::vector<std::thread> consumers
149     { consumer_count };
150 
151     // Run the benchmark 10 times and report the best time.
152     for (int times = 10; times--;)
153     {
154       S shared_data
155       { 100000, consumer_count };
156 
157       // Start the consumers.
158       for (unsigned i = 0; i < consumer_count; ++i)
159         consumers[i] = std::thread
160         { consumer_thread<S> , &shared_data };
161       // Start the producer and wait till it finishes.
162       std::thread
163       { producer_thread<S> , &shared_data }.join();
164       // Wait till consumers finish.
165       for (unsigned i = 0; i < consumer_count; ++i)
166         consumers[i].join();
167 
168       best_producer_time = std::min BOOST_PREVENT_MACRO_SUBSTITUTION (best_producer_time, shared_data.producer_time);
169 
170     }
171 
172     return best_producer_time;
173   }
174 
175 ////////////////////////////////////////////////////////////////////////////////////////////////
176 
177 } // namespace
178 
179 ////////////////////////////////////////////////////////////////////////////////////////////////
180 
181 // 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
182 
183 /*
184 
185  Producer-consumer ping-pong tests. It aims to benchmark condition variables with and without
186  thread cancellation support by comparing the time it took to complete the benchmark.
187 
188  Condition variable with thread cancellation support is boost::condition_variable from
189  boost-1.51. Without - std::condition_variable that comes with gcc-4.7.2.
190 
191  One producer, one to CONSUMER_MAX consumers. The benchmark calls
192  condition_variable::notify_all() without holding a mutex to maximize contention within this
193  function. Each benchmark for a number of consumers is run three times and the best time is
194  picked to get rid of outliers.
195 
196  The results are reported for each benchmark for a number of consumers. The most important number
197  is (std - boost) / std * 100. Positive numbers are when boost::condition_variable is faster,
198  negative it is slower.
199 
200  */
201 
main()202 int main()
203 {
204   std::printf("MAIN\n");
205   enum
206   {
207     CONSUMER_MAX = 2
208   };
209 
210   struct
211   {
212     Stopwatch::rep boost, std;
213   } best_times[CONSUMER_MAX] = {};
214 
215   for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
216   {
217     auto& b = best_times[i - 1];
218     std::printf("STD: %d\n", i);
219     b.std = benchmark_ping_pong<StdTypes> (i);
220     std::printf("BOOST: %d\n", i);
221     b.boost = benchmark_ping_pong<BoostTypes> (i);
222 
223     std::printf("consumers:                 %4d\n", i);
224     std::printf("best std producer time:   %15.9fsec\n", b.std * 1e-9);
225     std::printf("best boost producer time: %15.9fsec\n", b.boost * 1e-9);
226     std::printf("(std - boost) / std:       %7.2f%%\n", (b.std - b.boost) * 100. / b.std);
227   }
228 
229   printf("\ncsv:\n\n");
230   printf("consumers,(std-boost)/std,std,boost\n");
231   for (unsigned i = 1; i <= CONSUMER_MAX; ++i)
232   {
233     auto& b = best_times[i - 1];
234     printf("%d,%f,%lld,%lld\n", i, (b.std - b.boost) * 100. / b.std, b.std, b.boost);
235   }
236   return 1;
237 }
238