• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Comparison of bounded buffers based on different containers.
2 
3 // Copyright (c) 2003-2008 Jan Gaspar
4 // Copyright 2013 Paul A. Bristow.  Added some Quickbook snippet markers.
5 
6 // Use, modification, and distribution is subject to the Boost Software
7 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 #include <boost/circular_buffer.hpp>
11 #include <boost/thread/mutex.hpp>
12 #include <boost/thread/condition.hpp>
13 #include <boost/thread/thread.hpp>
14 #include <boost/timer/timer.hpp>
15 #include <boost/call_traits.hpp>
16 #include <boost/bind.hpp>
17 #include <deque>
18 #include <list>
19 #include <string>
20 #include <iostream>
21 
22 const unsigned long QUEUE_SIZE     = 1000L;
23 const unsigned long TOTAL_ELEMENTS = QUEUE_SIZE * 1000L;
24 
25 template <class T>
26 class bounded_buffer {
27 public:
28 
29     typedef boost::circular_buffer<T> container_type;
30     typedef typename container_type::size_type size_type;
31     typedef typename container_type::value_type value_type;
32     typedef typename boost::call_traits<value_type>::param_type param_type;
33 
bounded_buffer(size_type capacity)34     explicit bounded_buffer(size_type capacity) : m_unread(0), m_container(capacity) {}
35 
push_front(param_type item)36     void push_front(param_type item) {
37         boost::mutex::scoped_lock lock(m_mutex);
38         m_not_full.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_full, this));
39         m_container.push_front(item);
40         ++m_unread;
41         lock.unlock();
42         m_not_empty.notify_one();
43     }
44 
pop_back(value_type * pItem)45     void pop_back(value_type* pItem) {
46         boost::mutex::scoped_lock lock(m_mutex);
47         m_not_empty.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_empty, this));
48         *pItem = m_container[--m_unread];
49         lock.unlock();
50         m_not_full.notify_one();
51     }
52 
53 private:
54     bounded_buffer(const bounded_buffer&);              // Disabled copy constructor
55     bounded_buffer& operator = (const bounded_buffer&); // Disabled assign operator
56 
is_not_empty() const57     bool is_not_empty() const { return m_unread > 0; }
is_not_full() const58     bool is_not_full() const { return m_unread < m_container.capacity(); }
59 
60     size_type m_unread;
61     container_type m_container;
62     boost::mutex m_mutex;
63     boost::condition m_not_empty;
64     boost::condition m_not_full;
65 };
66 
67 template <class T>
68 class bounded_buffer_space_optimized {
69 public:
70 
71     typedef boost::circular_buffer_space_optimized<T> container_type;
72     typedef typename container_type::size_type size_type;
73     typedef typename container_type::value_type value_type;
74     typedef typename boost::call_traits<value_type>::param_type param_type;
75 
bounded_buffer_space_optimized(size_type capacity)76     explicit bounded_buffer_space_optimized(size_type capacity) : m_container(capacity) {}
77 
push_front(param_type item)78     void push_front(param_type item) {
79         boost::mutex::scoped_lock lock(m_mutex);
80         m_not_full.wait(lock, boost::bind(&bounded_buffer_space_optimized<value_type>::is_not_full, this));
81         m_container.push_front(item);
82         lock.unlock();
83         m_not_empty.notify_one();
84     }
85 
pop_back(value_type * pItem)86     void pop_back(value_type* pItem) {
87         boost::mutex::scoped_lock lock(m_mutex);
88         m_not_empty.wait(lock, boost::bind(&bounded_buffer_space_optimized<value_type>::is_not_empty, this));
89         *pItem = m_container.back();
90         m_container.pop_back();
91         lock.unlock();
92         m_not_full.notify_one();
93     }
94 
95 private:
96 
97     bounded_buffer_space_optimized(const bounded_buffer_space_optimized&);              // Disabled copy constructor
98     bounded_buffer_space_optimized& operator = (const bounded_buffer_space_optimized&); // Disabled assign operator
99 
is_not_empty() const100     bool is_not_empty() const { return m_container.size() > 0; }
is_not_full() const101     bool is_not_full() const { return m_container.size() < m_container.capacity(); }
102 
103     container_type m_container;
104     boost::mutex m_mutex;
105     boost::condition m_not_empty;
106     boost::condition m_not_full;
107 };
108 
109 template <class T>
110 class bounded_buffer_deque_based {
111 public:
112 
113     typedef std::deque<T> container_type;
114     typedef typename container_type::size_type size_type;
115     typedef typename container_type::value_type value_type;
116     typedef typename boost::call_traits<value_type>::param_type param_type;
117 
bounded_buffer_deque_based(size_type capacity)118     explicit bounded_buffer_deque_based(size_type capacity) : m_capacity(capacity) {}
119 
push_front(param_type item)120     void push_front(param_type item) {
121         boost::mutex::scoped_lock lock(m_mutex);
122         m_not_full.wait(lock, boost::bind(&bounded_buffer_deque_based<value_type>::is_not_full, this));
123         m_container.push_front(item);
124         lock.unlock();
125         m_not_empty.notify_one();
126     }
127 
pop_back(value_type * pItem)128     void pop_back(value_type* pItem) {
129         boost::mutex::scoped_lock lock(m_mutex);
130         m_not_empty.wait(lock, boost::bind(&bounded_buffer_deque_based<value_type>::is_not_empty, this));
131         *pItem = m_container.back();
132         m_container.pop_back();
133         lock.unlock();
134         m_not_full.notify_one();
135     }
136 
137 private:
138 
139     bounded_buffer_deque_based(const bounded_buffer_deque_based&);              // Disabled copy constructor
140     bounded_buffer_deque_based& operator = (const bounded_buffer_deque_based&); // Disabled assign operator
141 
is_not_empty() const142     bool is_not_empty() const { return m_container.size() > 0; }
is_not_full() const143     bool is_not_full() const { return m_container.size() < m_capacity; }
144 
145     const size_type m_capacity;
146     container_type m_container;
147     boost::mutex m_mutex;
148     boost::condition m_not_empty;
149     boost::condition m_not_full;
150 };
151 
152 template <class T>
153 class bounded_buffer_list_based {
154 public:
155 
156     typedef std::list<T> container_type;
157     typedef typename container_type::size_type size_type;
158     typedef typename container_type::value_type value_type;
159     typedef typename boost::call_traits<value_type>::param_type param_type;
160 
bounded_buffer_list_based(size_type capacity)161     explicit bounded_buffer_list_based(size_type capacity) : m_capacity(capacity) {}
162 
push_front(param_type item)163     void push_front(param_type item) {
164         boost::mutex::scoped_lock lock(m_mutex);
165         m_not_full.wait(lock, boost::bind(&bounded_buffer_list_based<value_type>::is_not_full, this));
166         m_container.push_front(item);
167         lock.unlock();
168         m_not_empty.notify_one();
169     }
170 
pop_back(value_type * pItem)171     void pop_back(value_type* pItem) {
172         boost::mutex::scoped_lock lock(m_mutex);
173         m_not_empty.wait(lock, boost::bind(&bounded_buffer_list_based<value_type>::is_not_empty, this));
174         *pItem = m_container.back();
175         m_container.pop_back();
176         lock.unlock();
177         m_not_full.notify_one();
178     }
179 
180 private:
181 
182     bounded_buffer_list_based(const bounded_buffer_list_based&);              // Disabled copy constructor
183     bounded_buffer_list_based& operator = (const bounded_buffer_list_based&); // Disabled assign operator
184 
is_not_empty() const185     bool is_not_empty() const { return m_container.size() > 0; }
is_not_full() const186     bool is_not_full() const { return m_container.size() < m_capacity; }
187 
188     const size_type m_capacity;
189     container_type m_container;
190     boost::mutex m_mutex;
191     boost::condition m_not_empty;
192     boost::condition m_not_full;
193 };
194 
195 template<class Buffer>
196 class Consumer {
197 
198     typedef typename Buffer::value_type value_type;
199     Buffer* m_container;
200     value_type m_item;
201 
202 public:
Consumer(Buffer * buffer)203     Consumer(Buffer* buffer) : m_container(buffer) {}
204 
operator ()()205     void operator()() {
206         for (unsigned long i = 0L; i < TOTAL_ELEMENTS; ++i) {
207             m_container->pop_back(&m_item);
208         }
209     }
210 };
211 
212 template<class Buffer>
213 class Producer {
214 
215     typedef typename Buffer::value_type value_type;
216     Buffer* m_container;
217 
218 public:
Producer(Buffer * buffer)219     Producer(Buffer* buffer) : m_container(buffer) {}
220 
operator ()()221     void operator()() {
222         for (unsigned long i = 0L; i < TOTAL_ELEMENTS; ++i) {
223             m_container->push_front(value_type());
224         }
225     }
226 };
227 
228 template<class Buffer>
fifo_test(Buffer * buffer)229 void fifo_test(Buffer* buffer) {
230 
231     // Start of measurement
232     boost::timer::auto_cpu_timer progress;
233 
234     // Initialize the buffer with some values before launching producer and consumer threads.
235     for (unsigned long i = QUEUE_SIZE / 2L; i > 0; --i) {
236 #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x581))
237         buffer->push_front(Buffer::value_type());
238 #else
239         buffer->push_front(BOOST_DEDUCED_TYPENAME Buffer::value_type());
240 #endif
241     }
242 
243     Consumer<Buffer> consumer(buffer);
244     Producer<Buffer> producer(buffer);
245 
246     // Start the threads.
247     boost::thread consume(consumer);
248     boost::thread produce(producer);
249 
250     // Wait for completion.
251     consume.join();
252     produce.join();
253 
254     // End of measurement
255 }
256 
main(int,char * [])257 int main(int /*argc*/, char* /*argv*/[]) {
258 
259     bounded_buffer<int> bb_int(QUEUE_SIZE);
260     std::cout << "bounded_buffer<int> ";
261     fifo_test(&bb_int);
262 
263     bounded_buffer_space_optimized<int> bb_space_optimized_int(QUEUE_SIZE);
264     std::cout << "bounded_buffer_space_optimized<int> ";
265     fifo_test(&bb_space_optimized_int);
266 
267     bounded_buffer_deque_based<int> bb_deque_based_int(QUEUE_SIZE);
268     std::cout << "bounded_buffer_deque_based<int> ";
269     fifo_test(&bb_deque_based_int);
270 
271     bounded_buffer_list_based<int> bb_list_based_int(QUEUE_SIZE);
272     std::cout << "bounded_buffer_list_based<int> ";
273     fifo_test(&bb_list_based_int);
274 
275     bounded_buffer<std::string> bb_string(QUEUE_SIZE);
276     std::cout << "bounded_buffer<std::string> ";
277     fifo_test(&bb_string);
278 
279     bounded_buffer_space_optimized<std::string> bb_space_optimized_string(QUEUE_SIZE);
280     std::cout << "bounded_buffer_space_optimized<std::string> ";
281     fifo_test(&bb_space_optimized_string);
282 
283     bounded_buffer_deque_based<std::string> bb_deque_based_string(QUEUE_SIZE);
284     std::cout << "bounded_buffer_deque_based<std::string> ";
285     fifo_test(&bb_deque_based_string);
286 
287     bounded_buffer_list_based<std::string> bb_list_based_string(QUEUE_SIZE);
288     std::cout << "bounded_buffer_list_based<std::string> ";
289     fifo_test(&bb_list_based_string);
290 
291     return 0;
292 }
293 /*
294 
295 //[bounded_buffer_comparison_output
296 
297   Description: Autorun "J:\Cpp\Misc\Debug\bounded_buffer_comparison.exe"
298   bounded_buffer<int> 5.15 s
299 
300   bounded_buffer_space_optimized<int> 5.71 s
301 
302   bounded_buffer_deque_based<int> 15.57 s
303 
304   bounded_buffer_list_based<int> 17.33 s
305 
306   bounded_buffer<std::string> 24.49 s
307 
308   bounded_buffer_space_optimized<std::string> 28.33 s
309 
310   bounded_buffer_deque_based<std::string> 29.45 s
311 
312   bounded_buffer_list_based<std::string> 31.29 s
313 
314   //] //[bounded_buffer_comparison_output]
315 
316 */
317