Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/multi_index/perf/test_perf.cpp @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 13.9 KB
Line 
1/* Boost.MultiIndex performance test.
2 *
3 * Copyright 2003-2006 Joaquín M López Muñoz.
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or copy at
6 * http://www.boost.org/LICENSE_1_0.txt)
7 *
8 * See http://www.boost.org/libs/multi_index for library home page.
9 */
10
11#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
12
13#include <algorithm>
14#include <assert.h>
15#include <boost/multi_index_container.hpp>
16#include <boost/multi_index/identity.hpp>
17#include <boost/multi_index/ordered_index.hpp>
18#include <boost/multi_index/sequenced_index.hpp>
19#include <climits>
20#include <ctime>
21#include <iomanip>
22#include <iostream>
23#include <list>
24#include <set>
25#include <string>
26#include <vector>
27
28using namespace std;
29using namespace boost::multi_index;
30
31/* Measurement harness by Andrew Koenig, extracted from companion code to
32 *   Stroustrup, B.: "Wrapping C++ Member Function Calls", The C++ Report,
33 *     June 2000, Vol 12/No 6.
34 * Original code retrievable at: http://www.research.att.com/~bs/wrap_code.cpp
35 */
36
37// How many clock units does it take to interrogate the clock?
38static double clock_overhead()
39{
40    clock_t k = clock(), start, limit;
41
42    // Wait for the clock to tick
43    do start = clock();
44    while (start == k);
45
46    // interrogate the clock until it has advanced at least a second
47    // (for reasonable accuracy)
48    limit = start + CLOCKS_PER_SEC;
49
50    unsigned long r = 0;
51    while ((k = clock()) < limit)
52        ++r;
53
54    return double(k - start) / r;
55}
56
57// We'd like the odds to be factor:1 that the result is
58// within percent% of the median
59const int factor = 10;
60const int percent = 20;
61
62// Measure a function (object) factor*2 times,
63// appending the measurements to the second argument
64template<class F>
65void measure_aux(F f, vector<double>& mv)
66{
67    static double ovhd = clock_overhead();
68
69    // Ensure we don't reallocate in mid-measurement
70    mv.reserve(mv.size() + factor*2);
71
72    // Wait for the clock to tick
73    clock_t k = clock();
74    clock_t start;
75
76    do start = clock();
77    while (start == k);
78
79    // Do 2*factor measurements
80    for (int i = 2*factor; i; --i) {
81        unsigned long count = 0, limit = 1, tcount = 0;
82
83        // Original code used CLOCKS_PER_SEC/100
84        const clock_t clocklimit = start + CLOCKS_PER_SEC/10;
85        clock_t t;
86
87        do {
88            while (count < limit) {
89                f();
90                ++count;
91            }
92            limit *= 2;
93            ++tcount;
94        } while ((t = clock()) < clocklimit);
95
96        // Wait for the clock to tick again;
97        clock_t t2;
98        do ++tcount;
99        while ((t2 = clock()) == t);
100
101        // Append the measurement to the vector
102        mv.push_back(((t2 - start) - (tcount * ovhd)) / count);
103
104        // Establish a new starting point
105        start = t2;
106    }
107}
108
109// Returns the number of clock units per iteration
110// With odds of factor:1, the measurement is within percent% of
111// the value returned, which is also the median of all measurements.
112template<class F>
113double measure(F f)
114{
115    vector<double> mv;
116
117    int n = 0;                        // iteration counter
118    do {
119        ++n;
120
121        // Try 2*factor measurements
122        measure_aux(f, mv);
123        assert(mv.size() == 2*n*factor);
124
125        // Compute the median.  We know the size is even, so we cheat.
126        sort(mv.begin(), mv.end());
127        double median = (mv[n*factor] + mv[n*factor-1])/2;
128
129        // If the extrema are within threshold of the median, we're done
130        if (mv[n] > (median * (100-percent))/100 &&
131            mv[mv.size() - n - 1] < (median * (100+percent))/100)
132            return median;
133
134    } while (mv.size() < factor * 200);
135
136    // Give up!
137    clog << "Help!\n\n";
138    exit(1);
139}
140
141/* dereferencing compare predicate */
142
143template <typename Iterator,typename Compare>
144struct it_compare
145{
146  bool operator()(const Iterator& x,const Iterator& y)const{return comp(*x,*y);}
147
148private:
149  Compare comp;
150};
151
152/* list_wrapper and multiset_wrapper adapt std::lists and std::multisets
153 * to make them conform to a set-like insert interface which test
154 * routines do assume.
155 */
156
157template <typename List>
158struct list_wrapper:List
159{
160  typedef typename List::value_type value_type;
161  typedef typename List::iterator   iterator;
162
163  pair<iterator,bool> insert(const value_type& v)
164  {
165    List::push_back(v);
166    return pair<iterator,bool>(--List::end(),true);
167  }
168};
169
170template <typename Multiset>
171struct multiset_wrapper:Multiset
172{
173  typedef typename Multiset::value_type value_type;
174  typedef typename Multiset::iterator   iterator;
175
176  pair<iterator,bool> insert(const value_type& v)
177  {
178    return pair<iterator,bool>(Multiset::insert(v),true);
179  }
180};
181
182/* space comsumption of manual simulations is determined by checking
183 * the node sizes of the containers involved. This cannot be done in a
184 * portable manner, so node_size has to be written on a per stdlibrary
185 * basis. Add your own versions if necessary.
186 */
187
188#if defined(BOOST_DINKUMWARE_STDLIB)
189
190template<typename Container>
191size_t node_size(const Container&)
192{
193  return sizeof(*Container().begin()._Mynode());
194}
195
196#elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
197
198template<typename Container>
199size_t node_size(const Container&)
200{
201  typedef typename Container::iterator::_Link_type node_ptr;
202  node_ptr p=0;
203  return sizeof(*p);
204}
205
206template<typename Value,typename Allocator>
207size_t node_size(const list<Value,Allocator>&)
208{
209  return sizeof(typename list<Value,Allocator>::iterator::_Node);
210}
211
212template<typename List>
213size_t node_size(const list_wrapper<List>&)
214{
215  return sizeof(typename List::iterator::_Node);
216}
217
218#else
219
220/* default version returns 0 by convention */
221
222template<typename Container>
223size_t node_size(const Container&)
224{
225  return 0;
226}
227
228#endif
229
230/* mono_container runs the tested routine on multi_index and manual
231 * simulations comprised of one standard container.
232 * bi_container and tri_container run the equivalent routine for manual
233 * compositions of two and three standard containers, respectively.
234 */
235
236template <typename Container>
237struct mono_container
238{
239  mono_container(int n_):n(n_){}
240
241  void operator()()
242  {
243    typedef typename Container::iterator iterator;
244
245    Container c;
246
247    for(int i=0;i<n;++i)c.insert(i);
248    for(iterator it=c.begin();it!=c.end();)c.erase(it++);
249  }
250
251  static size_t multi_index_node_size()
252  {
253    return sizeof(*Container().begin().get_node());
254  }
255
256  static size_t node_size()
257  {
258    return ::node_size(Container());
259  }
260
261private:
262  int n;
263};
264
265template <typename Container1,typename Container2>
266struct bi_container
267{
268  bi_container(int n_):n(n_){}
269
270  void operator()()
271  {
272    typedef typename Container1::iterator iterator1;
273    typedef typename Container2::iterator iterator2;
274
275    Container1 c1;
276    Container2 c2;
277
278    for(int i=0;i<n;++i){
279      iterator1 it1=c1.insert(i).first;
280      c2.insert(it1);
281    }
282    for(iterator2 it2=c2.begin();it2!=c2.end();)
283    {
284      c1.erase(*it2);
285      c2.erase(it2++);
286    }
287  }
288
289  static size_t node_size()
290  {
291    return ::node_size(Container1())+::node_size(Container2());
292  }
293
294private:
295  int n;
296};
297
298template <typename Container1,typename Container2,typename Container3>
299struct tri_container
300{
301  tri_container(int n_):n(n_){}
302
303  void operator()()
304  {
305    typedef typename Container1::iterator iterator1;
306    typedef typename Container2::iterator iterator2;
307    typedef typename Container3::iterator iterator3;
308
309    Container1 c1;
310    Container2 c2;
311    Container3 c3;
312
313    for(int i=0;i<n;++i){
314      iterator1 it1=c1.insert(i).first;
315      iterator2 it2=c2.insert(it1).first;
316      c3.insert(it2);
317    }
318    for(iterator3 it3=c3.begin();it3!=c3.end();)
319    {
320      c1.erase(**it3);
321      c2.erase(*it3);
322      c3.erase(it3++);
323    }
324  }
325
326  static size_t node_size()
327  {
328    return ::node_size(Container1())+
329           ::node_size(Container2())+::node_size(Container3());
330  }
331
332private:
333  int n;
334};
335
336/* measure and compare two routines for several numbers of elements
337 * and also estimates relative memory consumption.
338 */
339
340template <typename IndexedTest,typename ManualTest>
341void run_tests(
342  const char* title
343  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedTest)
344  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualTest))
345{
346  cout<<fixed<<setprecision(2);
347  cout<<title<<endl;
348  int n=1000;
349  for(int i=0;i<3;++i){
350    double indexed_t=measure(IndexedTest(n));
351    double manual_t=measure(ManualTest(n));
352    cout<<"  10^"<<i+3<<" elmts: "
353        <<setw(6)<<100.0*indexed_t/manual_t<<"% "
354        <<"("
355          <<setw(6)<<1000.0*indexed_t/CLOCKS_PER_SEC<<" ms / "
356          <<setw(6)<<1000.0*manual_t/CLOCKS_PER_SEC<<" ms)"
357        <<endl;
358    n*=10;
359  }
360
361  size_t indexed_t_node_size=IndexedTest::multi_index_node_size();
362  size_t manual_t_node_size=ManualTest::node_size();
363
364  if(manual_t_node_size){
365    cout<<"  space gain: "
366        <<setw(6)<<100.0*indexed_t_node_size/manual_t_node_size<<"%"<<endl;
367  }
368}
369
370/* compare_structures accept a multi_index_container instantiation and
371 * several standard containers, builds a manual simulation out of the
372 * latter and run the tests.
373 */
374
375template <typename IndexedType,typename ManualType>
376void compare_structures(
377  const char* title
378  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType)
379  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType))
380{
381  run_tests<
382    mono_container<IndexedType>,
383    mono_container<ManualType>
384  >(title);
385}
386
387template <typename IndexedType,typename ManualType1,typename ManualType2>
388void compare_structures2(
389  const char* title
390  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType)
391  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType1)
392  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType2))
393{
394  run_tests<
395    mono_container<IndexedType>,
396    bi_container<ManualType1,ManualType2>
397  >(title);
398}
399
400template <
401  typename IndexedType,
402  typename ManualType1,typename ManualType2,typename ManualType3
403>
404void compare_structures3(
405  const char* title
406  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType)
407  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType1)
408  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType2)
409  BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType3))
410{
411  run_tests<
412    mono_container<IndexedType>,
413    tri_container<ManualType1,ManualType2,ManualType3>
414  >(title);
415}
416
417int main()
418{
419  {
420    /* 1 ordered index */
421
422    typedef multi_index_container<int> indexed_t;
423    typedef set<int>                   manual_t;
424    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
425
426    compare_structures<indexed_t,manual_t>(
427      "1 ordered index");
428  }
429  {
430    /* 1 sequenced index */
431
432    typedef list_wrapper<
433      multi_index_container<
434        int,
435        indexed_by<sequenced<> > 
436      >
437    >                                  indexed_t;
438    typedef list_wrapper<list<int> >   manual_t;
439    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
440
441    compare_structures<indexed_t,manual_t>(
442      "1 sequenced index");
443  }
444  {
445    /* 2 ordered indices */
446
447    typedef multi_index_container<
448      int,
449      indexed_by<
450        ordered_unique<identity<int> >,
451        ordered_non_unique<identity<int> >
452      >
453    >                                  indexed_t;
454    typedef set<int>                   manual_t1;
455    typedef multiset<
456      manual_t1::iterator,
457      it_compare<
458        manual_t1::iterator,
459        manual_t1::key_compare
460      >
461    >                                  manual_t2;
462    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
463
464    compare_structures2<indexed_t,manual_t1,manual_t2>(
465      "2 ordered indices");
466  }
467  {
468    /* 1 ordered index + 1 sequenced index */
469
470    typedef multi_index_container<
471      int,
472      indexed_by<
473        boost::multi_index::ordered_unique<identity<int> >,
474        sequenced<>
475      >
476    >                                  indexed_t;
477    typedef list_wrapper<
478      list<int>
479    >                                  manual_t1;
480    typedef multiset<
481      manual_t1::iterator,
482      it_compare<
483        manual_t1::iterator,
484        std::less<int>
485      >
486    >                                  manual_t2;
487    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
488
489    compare_structures2<indexed_t,manual_t1,manual_t2>(
490      "1 ordered index + 1 sequenced index");
491  }
492  {
493    /* 3 ordered indices */
494
495    typedef multi_index_container<
496      int,
497      indexed_by<
498        ordered_unique<identity<int> >,
499        ordered_non_unique<identity<int> >,
500        ordered_non_unique<identity<int> >
501      >
502    >                                  indexed_t;
503    typedef set<int>                   manual_t1;
504    typedef multiset_wrapper<
505      multiset<
506        manual_t1::iterator,
507        it_compare<
508          manual_t1::iterator,
509          manual_t1::key_compare
510        >
511      >
512    >                                  manual_t2;
513    typedef multiset<
514      manual_t2::iterator,
515      it_compare<
516        manual_t2::iterator,
517        manual_t2::key_compare
518      >
519    >                                  manual_t3;
520    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
521
522    compare_structures3<indexed_t,manual_t1,manual_t2,manual_t3>(
523      "3 ordered indices");
524  }
525  {
526    /* 2 ordered indices + 1 sequenced index */
527
528    typedef multi_index_container<
529      int,
530      indexed_by<
531        ordered_unique<identity<int> >,
532        ordered_non_unique<identity<int> >,
533        sequenced<>
534      >
535    >                                  indexed_t;
536    typedef list_wrapper<
537      list<int>
538    >                                  manual_t1;
539    typedef multiset_wrapper<
540      multiset<
541        manual_t1::iterator,
542        it_compare<
543          manual_t1::iterator,
544          std::less<int>
545        >
546      >
547    >                                  manual_t2;
548    typedef multiset<
549      manual_t2::iterator,
550      it_compare<
551        manual_t2::iterator,
552        manual_t2::key_compare
553      >
554    >                                  manual_t3;
555    indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ 
556
557    compare_structures3<indexed_t,manual_t1,manual_t2,manual_t3>(
558      "2 ordered indices + 1 sequenced index");
559  }
560
561  return 0;
562}
Note: See TracBrowser for help on using the repository browser.