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
26namespace
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
83 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>
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>
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>
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
202int main()
203{
204 std::printf(format: "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(format: "STD: %d\n", i);
219 b.std = benchmark_ping_pong<StdTypes> (i);
220 std::printf(format: "BOOST: %d\n", i);
221 b.boost = benchmark_ping_pong<BoostTypes> (i);
222
223 std::printf(format: "consumers: %4d\n", i);
224 std::printf(format: "best std producer time: %15.9fsec\n", b.std * 1e-9);
225 std::printf(format: "best boost producer time: %15.9fsec\n", b.boost * 1e-9);
226 std::printf(format: "(std - boost) / std: %7.2f%%\n", (b.std - b.boost) * 100. / b.std);
227 }
228
229 printf(format: "\ncsv:\n\n");
230 printf(format: "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

source code of boost/libs/thread/example/perf_condition_variable.cpp