Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/multi_index/perf/test_perf.cpp @ 12

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

added boost

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