1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2007-2013. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // See http://www.boost.org/libs/container for documentation.
8 //
9 //////////////////////////////////////////////////////////////////////////////
10 //Enable checks in debug mode
11 #ifndef NDEBUG
12 #define BOOST_CONTAINER_ADAPTIVE_NODE_POOL_CHECK_INVARIANTS
13 #endif
14 
15 #ifdef _MSC_VER
16 #pragma warning (disable : 4512)
17 #pragma warning (disable : 4127)
18 #pragma warning (disable : 4244)
19 #pragma warning (disable : 4267)
20 #endif
21 
22 #include <boost/container/adaptive_pool.hpp>
23 #include <boost/container/node_allocator.hpp>
24 #include <boost/container/allocator.hpp>
25 #include <boost/container/list.hpp>
26 #include <memory>    //std::allocator
27 #include <iostream>  //std::cout, std::endl
28 #include <vector>    //std::vector
29 #include <cstddef>   //std::size_t
30 #include <cassert>   //assert
31 
32 #include <boost/timer/timer.hpp>
33 using boost::timer::cpu_timer;
34 using boost::timer::cpu_times;
35 using boost::timer::nanosecond_type;
36 
37 namespace bc = boost::container;
38 
39 typedef std::allocator<int>         StdAllocator;
40 typedef bc::allocator<int, 2>       AllocatorPlusV2;
41 typedef bc::allocator<int, 1>       AllocatorPlusV1;
42 typedef bc::adaptive_pool
43    < int
44    , bc::ADP_nodes_per_block
45    , bc::ADP_max_free_blocks
46    , bc::ADP_only_alignment
47    , 1>                             AdPoolAlignOnlyV1;
48 typedef bc::adaptive_pool
49    < int
50    , bc::ADP_nodes_per_block
51    , bc::ADP_max_free_blocks
52    , bc::ADP_only_alignment
53    , 2>                             AdPoolAlignOnlyV2;
54 typedef bc::adaptive_pool
55    < int
56    , bc::ADP_nodes_per_block
57    , bc::ADP_max_free_blocks
58    , 2
59    , 1>                             AdPool2PercentV1;
60 typedef bc::adaptive_pool
61    < int
62    , bc::ADP_nodes_per_block
63    , bc::ADP_max_free_blocks
64    , 2
65    , 2>                             AdPool2PercentV2;
66 typedef bc::node_allocator
67    < int
68    , bc::NodeAlloc_nodes_per_block
69    , 1>                             SimpleSegregatedStorageV1;
70 typedef bc::node_allocator
71    < int
72    , bc::NodeAlloc_nodes_per_block
73    , 2>                             SimpleSegregatedStorageV2;
74 
75 //Explicit instantiation
76 template class bc::adaptive_pool
77    < int
78    , bc::ADP_nodes_per_block
79    , bc::ADP_max_free_blocks
80    , bc::ADP_only_alignment
81    , 2>;
82 
83 template class bc::node_allocator
84    < int
85    , bc::NodeAlloc_nodes_per_block
86    , 2>;
87 
88 template<class Allocator> struct get_allocator_name;
89 
90 template<> struct get_allocator_name<StdAllocator>
getget_allocator_name91 {  static const char *get() {  return "StdAllocator";  } };
92 
93 template<> struct get_allocator_name<AllocatorPlusV2>
getget_allocator_name94 {  static const char *get() {  return "AllocatorPlusV2";  } };
95 
96 template<> struct get_allocator_name<AllocatorPlusV1>
getget_allocator_name97 {  static const char *get() {  return "AllocatorPlusV1";  } };
98 
99 template<> struct get_allocator_name<AdPoolAlignOnlyV1>
getget_allocator_name100 {  static const char *get() {  return "AdPoolAlignOnlyV1";  } };
101 
102 template<> struct get_allocator_name<AdPoolAlignOnlyV2>
getget_allocator_name103 {  static const char *get() {  return "AdPoolAlignOnlyV2";  } };
104 
105 template<> struct get_allocator_name<AdPool2PercentV1>
getget_allocator_name106 {  static const char *get() {  return "AdPool2PercentV1";  } };
107 
108 template<> struct get_allocator_name<AdPool2PercentV2>
getget_allocator_name109 {  static const char *get() {  return "AdPool2PercentV2";  } };
110 
111 template<> struct get_allocator_name<SimpleSegregatedStorageV1>
getget_allocator_name112 {  static const char *get() {  return "SimpleSegregatedStorageV1";  } };
113 
114 template<> struct get_allocator_name<SimpleSegregatedStorageV2>
getget_allocator_name115 {  static const char *get() {  return "SimpleSegregatedStorageV2";  } };
116 
117 class MyInt
118 {
119    std::size_t int_;
120 
121    public:
MyInt(std::size_t i=0)122    explicit MyInt(std::size_t i = 0) : int_(i){}
MyInt(const MyInt & other)123    MyInt(const MyInt &other)
124       :  int_(other.int_)
125    {}
operator =(const MyInt & other)126    MyInt & operator=(const MyInt &other)
127    {
128       int_ = other.int_;
129       return *this;
130    }
131 };
132 
133 template<class Allocator>
list_test_template(std::size_t num_iterations,std::size_t num_elements,bool csv_output)134 void list_test_template(std::size_t num_iterations, std::size_t num_elements, bool csv_output)
135 {
136    typedef typename Allocator::template rebind<MyInt>::other IntAllocator;
137    nanosecond_type tinsert, terase;
138    bc::dlmalloc_malloc_stats_t insert_stats, erase_stats;
139    std::size_t insert_inuse, erase_inuse;
140    const size_t sizeof_node = 2*sizeof(void*)+sizeof(int);
141 
142    typedef bc::list<MyInt, IntAllocator>  list_t;
143    typedef typename list_t::iterator      iterator_t;
144    {
145       cpu_timer timer;
146       timer.resume();
147       list_t l;
148       for(std::size_t r = 0; r != num_iterations; ++r){
149          l.insert(l.end(), num_elements, MyInt(r));
150       }
151       timer.stop();
152       tinsert = timer.elapsed().wall;
153 
154       insert_inuse = bc::dlmalloc_in_use_memory();
155       insert_stats = bc::dlmalloc_malloc_stats();
156 /*
157       iterator_t it(l.begin());
158       iterator_t last(--l.end());
159       for(std::size_t n_elem = 0, n_max = l.size()/2-1; n_elem != n_max; ++n_elem)
160       {
161          l.splice(it++, l, last--);
162       }
163 */
164       //l.reverse();
165 
166       //Now preprocess erase ranges
167       std::vector<iterator_t> ranges_to_erase;
168       ranges_to_erase.push_back(l.begin());
169       for(std::size_t r = 0; r != num_iterations; ++r){
170          iterator_t next_pos(ranges_to_erase[r]);
171          std::size_t n = num_elements;
172          while(n--){ ++next_pos; }
173          ranges_to_erase.push_back(next_pos);
174       }
175 
176       //Measure range erasure function
177       timer.start();
178       for(std::size_t r = 0; r != num_iterations; ++r){
179          assert((r+1) < ranges_to_erase.size());
180          l.erase(ranges_to_erase[r], ranges_to_erase[r+1]);
181       }
182       timer.stop();
183       terase = timer.elapsed().wall;
184       erase_inuse = bc::dlmalloc_in_use_memory();
185       erase_stats = bc::dlmalloc_malloc_stats();
186    }
187 
188 
189    if(csv_output){
190       std::cout   << get_allocator_name<Allocator>::get()
191                   << ";"
192                   << num_iterations
193                   << ";"
194                   << num_elements
195                   << ";"
196                   << float(tinsert)/(num_iterations*num_elements)
197                   << ";"
198                   << (unsigned int)insert_stats.system_bytes
199                   << ";"
200                   << float(insert_stats.system_bytes)/(num_iterations*num_elements*sizeof_node)*100.0-100.0
201                   << ";"
202                   << (unsigned int)insert_inuse
203                   << ";"
204                   << (float(insert_inuse)/(num_iterations*num_elements*sizeof_node)*100.0)-100.0
205                   << ";";
206    std::cout   << float(terase)/(num_iterations*num_elements)
207                << ";"
208                << (unsigned int)erase_stats.system_bytes
209                << ";"
210                << (unsigned int)erase_inuse
211                << std::endl;
212    }
213    else{
214       std::cout << std::endl
215                << "Allocator: " << get_allocator_name<Allocator>::get()
216                << std::endl
217                << "  allocation/deallocation(ns): " << float(tinsert)/(num_iterations*num_elements) <<  '\t' << float(terase)/(num_iterations*num_elements)
218                << std::endl
219                << "  Sys MB(overh.)/Inuse MB(overh.): " << (float)insert_stats.system_bytes/(1024*1024) << "(" << float(insert_stats.system_bytes)/(num_iterations*num_elements*sizeof_node)*100.0-100.0 << "%)"
220                << " / "
221                << (float)insert_inuse/(1024*1024) << "(" << (float(insert_inuse)/(num_iterations*num_elements*sizeof_node)*100.0)-100.0 << "%)"
222                << std::endl
223                << "  system MB/inuse bytes after:    " << (float)erase_stats.system_bytes/(1024*1024) << '\t' << bc::dlmalloc_in_use_memory()
224                << std::endl  << std::endl;
225    }
226 
227    //Release node_allocator cache
228    typedef boost::container::dtl::shared_node_pool
229       < (2*sizeof(void*)+sizeof(int))
230       , AdPoolAlignOnlyV2::nodes_per_block> shared_node_pool_t;
231    boost::container::dtl::singleton_default
232       <shared_node_pool_t>::instance().purge_blocks();
233 
234    //Release adaptive_pool cache
235    typedef boost::container::dtl::shared_adaptive_node_pool
236       < (2*sizeof(void*)+sizeof(int))
237       , AdPool2PercentV2::nodes_per_block
238       , AdPool2PercentV2::max_free_blocks
239       , AdPool2PercentV2::overhead_percent> shared_adaptive_pool_plus_t;
240    boost::container::dtl::singleton_default
241       <shared_adaptive_pool_plus_t>::instance().deallocate_free_blocks();
242 
243    //Release adaptive_pool cache
244    typedef boost::container::dtl::shared_adaptive_node_pool
245       < (2*sizeof(void*)+sizeof(int))
246       , AdPool2PercentV2::nodes_per_block
247       , AdPool2PercentV2::max_free_blocks
248       , 0u> shared_adaptive_pool_plus_align_only_t;
249    boost::container::dtl::singleton_default
250       <shared_adaptive_pool_plus_align_only_t>::instance().deallocate_free_blocks();
251    //Release dlmalloc memory
252    bc::dlmalloc_trim(0);
253 }
254 
print_header()255 void print_header()
256 {
257    std::cout   << "Allocator" << ";" << "Iterations" << ";" << "Size" << ";"
258                << "Insertion time(ns)" << ";"
259                << "System bytes" << ";"
260                << "System overhead(%)" << ";"
261                << "In use bytes" << ";"
262                << "In use overhead(%)" << ";"
263                << "Erasure time (ns)" << ";"
264                << "System bytes after" << ";"
265                << "In use bytes after"
266                << std::endl;
267 }
268 
main(int argc,const char * argv[])269 int main(int argc, const char *argv[])
270 {
271    //#define SINGLE_TEST
272    #define SIMPLE_IT
273    #ifdef SINGLE_TEST
274       #ifdef BOOST_CONTAINER_ADAPTIVE_NODE_POOL_CHECK_INVARIANTS
275       std::size_t numrep[] = { 1000 };
276       #elif defined(NDEBUG)
277       std::size_t numrep [] = { 15000 };
278       #else
279       std::size_t numrep [] = { 1000 };
280       #endif
281       std::size_t numele [] = { 100 };
282    #elif defined(SIMPLE_IT)
283       std::size_t numrep [] = { 3 };
284       std::size_t numele [] = { 100 };
285    #else
286       #ifdef NDEBUG
287       std::size_t numrep [] = { 300, 3000, 30000, 300000, 600000, 1500000, 3000000 };
288       #else
289       std::size_t numrep [] = { 20,   200, 2000, 20000, 40000, 100000, 200000 };
290       #endif
291       std::size_t numele [] = { 10000, 1000, 100, 10, 5, 2, 1     };
292    #endif
293 
294    bool csv_output = argc == 2 && (strcmp(argv[1], "--csv-output") == 0);
295 
296    if(csv_output){/*
297       print_header();
298       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
299          list_test_template<AllocatorPlusV1>(numrep[i], numele[i], csv_output);
300       }
301       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
302          list_test_template<AllocatorPlusV2>(numrep[i], numele[i], csv_output);
303       }
304       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
305          list_test_template<AdPoolAlignOnlyV1>(numrep[i], numele[i], csv_output);
306       }
307       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
308          list_test_template<AdPoolAlignOnlyV2>(numrep[i], numele[i], csv_output);
309       }
310       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
311          list_test_template<AdPool2PercentV1>(numrep[i], numele[i], csv_output);
312       }
313       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
314          list_test_template<AdPool2PercentV2>(numrep[i], numele[i], csv_output);
315       }
316       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
317          list_test_template<SimpleSegregatedStorageV1>(numrep[i], numele[i], csv_output);
318       }
319       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
320          list_test_template<SimpleSegregatedStorageV2>(numrep[i], numele[i], csv_output);
321       }*/
322    }
323    else{
324       for(std::size_t i = 0; i < sizeof(numele)/sizeof(numele[0]); ++i){
325          std::cout   << "\n    -----------------------------------    \n"
326                      <<   "  Iterations/Elements:         " << numrep[i] << "/" << numele[i]
327                      << "\n    -----------------------------------    \n";
328          list_test_template<AllocatorPlusV1>(numrep[i], numele[i], csv_output);
329          list_test_template<AllocatorPlusV2>(numrep[i], numele[i], csv_output);
330          list_test_template<AdPoolAlignOnlyV1>(numrep[i], numele[i], csv_output);
331          list_test_template<AdPoolAlignOnlyV2>(numrep[i], numele[i], csv_output);
332          list_test_template<AdPool2PercentV1>(numrep[i], numele[i], csv_output);
333          list_test_template<AdPool2PercentV2>(numrep[i], numele[i], csv_output);
334          list_test_template<SimpleSegregatedStorageV1>(numrep[i], numele[i], csv_output);
335          list_test_template<SimpleSegregatedStorageV2>(numrep[i], numele[i], csv_output);
336       }
337    }
338    return 0;
339 }
340