/* Boost.MultiIndex performance test. * * Copyright 2003-2013 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See http://www.boost.org/libs/multi_index for library home page. */ #include /* keep it first to prevent nasty warns in MSVC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace boost::multi_index; /* Measurement harness by Andrew Koenig, extracted from companion code to * Stroustrup, B.: "Wrapping C++ Member Function Calls", The C++ Report, * June 2000, Vol 12/No 6. * Original code retrievable at: http://www.research.att.com/~bs/wrap_code.cpp */ // How many clock units does it take to interrogate the clock? static double clock_overhead() { clock_t k = clock(), start, limit; // Wait for the clock to tick do start = clock(); while (start == k); // interrogate the clock until it has advanced at least a second // (for reasonable accuracy) limit = start + CLOCKS_PER_SEC; unsigned long r = 0; while ((k = clock()) < limit) ++r; return double(k - start) / r; } // We'd like the odds to be factor:1 that the result is // within percent% of the median const int factor = 10; const int percent = 20; // Measure a function (object) factor*2 times, // appending the measurements to the second argument template void measure_aux(F f, vector& mv) { static double ovhd = clock_overhead(); // Ensure we don't reallocate in mid-measurement mv.reserve(mv.size() + factor*2); // Wait for the clock to tick clock_t k = clock(); clock_t start; do start = clock(); while (start == k); // Do 2*factor measurements for (int i = 2*factor; i; --i) { unsigned long count = 0, limit = 1, tcount = 0; // Original code used CLOCKS_PER_SEC/100 const clock_t clocklimit = start + CLOCKS_PER_SEC/10; clock_t t; do { while (count < limit) { f(); ++count; } limit *= 2; ++tcount; } while ((t = clock()) < clocklimit); // Wait for the clock to tick again; clock_t t2; do ++tcount; while ((t2 = clock()) == t); // Append the measurement to the vector mv.push_back(((t2 - start) - (tcount * ovhd)) / count); // Establish a new starting point start = t2; } } // Returns the number of clock units per iteration // With odds of factor:1, the measurement is within percent% of // the value returned, which is also the median of all measurements. template double measure(F f) { vector mv; int n = 0; // iteration counter do { ++n; // Try 2*factor measurements measure_aux(f, mv); assert(mv.size() == 2*n*factor); // Compute the median. We know the size is even, so we cheat. sort(mv.begin(), mv.end()); double median = (mv[n*factor] + mv[n*factor-1])/2; // If the extrema are within threshold of the median, we're done if (mv[n] > (median * (100-percent))/100 && mv[mv.size() - n - 1] < (median * (100+percent))/100) return median; } while (mv.size() < factor * 200); // Give up! clog << "Help!\n\n"; exit(1); } /* dereferencing compare predicate */ template struct it_compare { bool operator()(const Iterator& x,const Iterator& y)const{return comp(*x,*y);} private: Compare comp; }; /* list_wrapper and multiset_wrapper adapt std::lists and std::multisets * to make them conform to a set-like insert interface which test * routines do assume. */ template struct list_wrapper:List { typedef typename List::value_type value_type; typedef typename List::iterator iterator; pair insert(const value_type& v) { List::push_back(v); return pair(boost::prior(List::end()),true); } }; template struct multiset_wrapper:Multiset { typedef typename Multiset::value_type value_type; typedef typename Multiset::iterator iterator; pair insert(const value_type& v) { return pair(Multiset::insert(v),true); } }; /* space comsumption of manual simulations is determined by checking * the node sizes of the containers involved. This cannot be done in a * portable manner, so node_size has to be written on a per stdlibrary * basis. Add your own versions if necessary. */ #if defined(BOOST_DINKUMWARE_STDLIB) template size_t node_size(const Container&) { return sizeof(*Container().begin()._Mynode()); } #elif defined(__GLIBCPP__) || defined(__GLIBCXX__) template size_t node_size(const Container&) { typedef typename Container::iterator::_Link_type node_ptr; node_ptr p=0; return sizeof(*p); } template size_t node_size(const list&) { return sizeof(typename list::iterator::_Node); } template size_t node_size(const list_wrapper&) { return sizeof(typename List::iterator::_Node); } #else /* default version returns 0 by convention */ template size_t node_size(const Container&) { return 0; } #endif /* mono_container runs the tested routine on multi_index and manual * simulations comprised of one standard container. * bi_container and tri_container run the equivalent routine for manual * compositions of two and three standard containers, respectively. */ template struct mono_container { mono_container(int n_):n(n_){} void operator()() { typedef typename Container::iterator iterator; Container c; for(int i=0;i struct bi_container { bi_container(int n_):n(n_){} void operator()() { typedef typename Container1::iterator iterator1; typedef typename Container2::iterator iterator2; Container1 c1; Container2 c2; for(int i=0;i struct tri_container { tri_container(int n_):n(n_){} void operator()() { typedef typename Container1::iterator iterator1; typedef typename Container2::iterator iterator2; typedef typename Container3::iterator iterator3; Container1 c1; Container2 c2; Container3 c3; for(int i=0;i void run_tests(const char* title) { cout< void compare_structures(const char* title) { run_tests< mono_container, mono_container >(title); } template void compare_structures2(const char* title) { run_tests< mono_container, bi_container >(title); } template < typename IndexedType, typename ManualType1,typename ManualType2,typename ManualType3 > void compare_structures3(const char* title) { run_tests< mono_container, tri_container >(title); } int main() { /* some stdlibs provide the discussed but finally rejected std::identity */ using boost::multi_index::identity; { /* 1 ordered index */ typedef multi_index_container indexed_t; typedef set manual_t; compare_structures( "1 ordered index"); } { /* 1 sequenced index */ typedef list_wrapper< multi_index_container< int, indexed_by > > > indexed_t; typedef list_wrapper > manual_t; compare_structures( "1 sequenced index"); } { /* 2 ordered indices */ typedef multi_index_container< int, indexed_by< ordered_unique >, ordered_non_unique > > > indexed_t; typedef set manual_t1; typedef multiset< manual_t1::iterator, it_compare< manual_t1::iterator, manual_t1::key_compare > > manual_t2; compare_structures2( "2 ordered indices"); } { /* 1 ordered index + 1 sequenced index */ typedef multi_index_container< int, indexed_by< boost::multi_index::ordered_unique >, sequenced<> > > indexed_t; typedef list_wrapper< list > manual_t1; typedef multiset< manual_t1::iterator, it_compare< manual_t1::iterator, std::less > > manual_t2; compare_structures2( "1 ordered index + 1 sequenced index"); } { /* 3 ordered indices */ typedef multi_index_container< int, indexed_by< ordered_unique >, ordered_non_unique >, ordered_non_unique > > > indexed_t; typedef set manual_t1; typedef multiset_wrapper< multiset< manual_t1::iterator, it_compare< manual_t1::iterator, manual_t1::key_compare > > > manual_t2; typedef multiset< manual_t2::iterator, it_compare< manual_t2::iterator, manual_t2::key_compare > > manual_t3; compare_structures3( "3 ordered indices"); } { /* 2 ordered indices + 1 sequenced index */ typedef multi_index_container< int, indexed_by< ordered_unique >, ordered_non_unique >, sequenced<> > > indexed_t; typedef list_wrapper< list > manual_t1; typedef multiset_wrapper< multiset< manual_t1::iterator, it_compare< manual_t1::iterator, std::less > > > manual_t2; typedef multiset< manual_t2::iterator, it_compare< manual_t2::iterator, manual_t2::key_compare > > manual_t3; compare_structures3( "2 ordered indices + 1 sequenced index"); } return 0; }