bounded_buffer_comparison.cpp 10 KB

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