diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/boost/libs/multi_index/perf/test_perf.cpp | |
parent | Initial commit. (diff) | |
download | ceph-upstream.tar.xz ceph-upstream.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/multi_index/perf/test_perf.cpp')
-rw-r--r-- | src/boost/libs/multi_index/perf/test_perf.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/src/boost/libs/multi_index/perf/test_perf.cpp b/src/boost/libs/multi_index/perf/test_perf.cpp new file mode 100644 index 000000000..7b2441787 --- /dev/null +++ b/src/boost/libs/multi_index/perf/test_perf.cpp @@ -0,0 +1,545 @@ +/* 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 <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ + +#include <algorithm> +#include <assert.h> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/identity.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/multi_index/sequenced_index.hpp> +#include <boost/next_prior.hpp> +#include <climits> +#include <ctime> +#include <iomanip> +#include <iostream> +#include <list> +#include <set> +#include <string> +#include <vector> + +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<class F> +void measure_aux(F f, vector<double>& 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<class F> +double measure(F f) +{ + vector<double> 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 <typename Iterator,typename Compare> +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 <typename List> +struct list_wrapper:List +{ + typedef typename List::value_type value_type; + typedef typename List::iterator iterator; + + pair<iterator,bool> insert(const value_type& v) + { + List::push_back(v); + return pair<iterator,bool>(boost::prior(List::end()),true); + } +}; + +template <typename Multiset> +struct multiset_wrapper:Multiset +{ + typedef typename Multiset::value_type value_type; + typedef typename Multiset::iterator iterator; + + pair<iterator,bool> insert(const value_type& v) + { + return pair<iterator,bool>(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<typename Container> +size_t node_size(const Container&) +{ + return sizeof(*Container().begin()._Mynode()); +} + +#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) + +template<typename Container> +size_t node_size(const Container&) +{ + typedef typename Container::iterator::_Link_type node_ptr; + node_ptr p=0; + return sizeof(*p); +} + +template<typename Value,typename Allocator> +size_t node_size(const list<Value,Allocator>&) +{ + return sizeof(typename list<Value,Allocator>::iterator::_Node); +} + +template<typename List> +size_t node_size(const list_wrapper<List>&) +{ + return sizeof(typename List::iterator::_Node); +} + +#else + +/* default version returns 0 by convention */ + +template<typename Container> +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 <typename Container> +struct mono_container +{ + mono_container(int n_):n(n_){} + + void operator()() + { + typedef typename Container::iterator iterator; + + Container c; + + for(int i=0;i<n;++i)c.insert(i); + for(iterator it=c.begin();it!=c.end();)c.erase(it++); + } + + static size_t multi_index_node_size() + { + return sizeof(*Container().begin().get_node()); + } + + static size_t node_size() + { + return ::node_size(Container()); + } + +private: + int n; +}; + +template <typename Container1,typename Container2> +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<n;++i){ + iterator1 it1=c1.insert(i).first; + c2.insert(it1); + } + for(iterator2 it2=c2.begin();it2!=c2.end();) + { + c1.erase(*it2); + c2.erase(it2++); + } + } + + static size_t node_size() + { + return ::node_size(Container1())+::node_size(Container2()); + } + +private: + int n; +}; + +template <typename Container1,typename Container2,typename Container3> +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<n;++i){ + iterator1 it1=c1.insert(i).first; + iterator2 it2=c2.insert(it1).first; + c3.insert(it2); + } + for(iterator3 it3=c3.begin();it3!=c3.end();) + { + c1.erase(**it3); + c2.erase(*it3); + c3.erase(it3++); + } + } + + static size_t node_size() + { + return ::node_size(Container1())+ + ::node_size(Container2())+::node_size(Container3()); + } + +private: + int n; +}; + +/* measure and compare two routines for several numbers of elements + * and also estimates relative memory consumption. + */ + +template <typename IndexedTest,typename ManualTest> +void run_tests(const char* title) +{ + cout<<fixed<<setprecision(2); + cout<<title<<endl; + int n=1000; + for(int i=0;i<3;++i){ + double indexed_t=measure(IndexedTest(n)); + double manual_t=measure(ManualTest(n)); + cout<<" 10^"<<i+3<<" elmts: " + <<setw(6)<<100.0*indexed_t/manual_t<<"% " + <<"(" + <<setw(6)<<1000.0*indexed_t/CLOCKS_PER_SEC<<" ms / " + <<setw(6)<<1000.0*manual_t/CLOCKS_PER_SEC<<" ms)" + <<endl; + n*=10; + } + + size_t indexed_t_node_size=IndexedTest::multi_index_node_size(); + size_t manual_t_node_size=ManualTest::node_size(); + + if(manual_t_node_size){ + cout<<" space gain: " + <<setw(6)<<100.0*indexed_t_node_size/manual_t_node_size<<"%"<<endl; + } +} + +/* compare_structures accept a multi_index_container instantiation and + * several standard containers, builds a manual simulation out of the + * latter and run the tests. + */ + +template <typename IndexedType,typename ManualType> +void compare_structures(const char* title) +{ + run_tests< + mono_container<IndexedType>, + mono_container<ManualType> + >(title); +} + +template <typename IndexedType,typename ManualType1,typename ManualType2> +void compare_structures2(const char* title) +{ + run_tests< + mono_container<IndexedType>, + bi_container<ManualType1,ManualType2> + >(title); +} + +template < + typename IndexedType, + typename ManualType1,typename ManualType2,typename ManualType3 +> +void compare_structures3(const char* title) +{ + run_tests< + mono_container<IndexedType>, + tri_container<ManualType1,ManualType2,ManualType3> + >(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<int> indexed_t; + typedef set<int> manual_t; + + compare_structures<indexed_t,manual_t>( + "1 ordered index"); + } + { + /* 1 sequenced index */ + + typedef list_wrapper< + multi_index_container< + int, + indexed_by<sequenced<> > + > + > indexed_t; + typedef list_wrapper<list<int> > manual_t; + + compare_structures<indexed_t,manual_t>( + "1 sequenced index"); + } + { + /* 2 ordered indices */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique<identity<int> >, + ordered_non_unique<identity<int> > + > + > indexed_t; + typedef set<int> manual_t1; + typedef multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + manual_t1::key_compare + > + > manual_t2; + + compare_structures2<indexed_t,manual_t1,manual_t2>( + "2 ordered indices"); + } + { + /* 1 ordered index + 1 sequenced index */ + + typedef multi_index_container< + int, + indexed_by< + boost::multi_index::ordered_unique<identity<int> >, + sequenced<> + > + > indexed_t; + typedef list_wrapper< + list<int> + > manual_t1; + typedef multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + std::less<int> + > + > manual_t2; + + compare_structures2<indexed_t,manual_t1,manual_t2>( + "1 ordered index + 1 sequenced index"); + } + { + /* 3 ordered indices */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique<identity<int> >, + ordered_non_unique<identity<int> >, + ordered_non_unique<identity<int> > + > + > indexed_t; + typedef set<int> 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<indexed_t,manual_t1,manual_t2,manual_t3>( + "3 ordered indices"); + } + { + /* 2 ordered indices + 1 sequenced index */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique<identity<int> >, + ordered_non_unique<identity<int> >, + sequenced<> + > + > indexed_t; + typedef list_wrapper< + list<int> + > manual_t1; + typedef multiset_wrapper< + multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + std::less<int> + > + > + > manual_t2; + typedef multiset< + manual_t2::iterator, + it_compare< + manual_t2::iterator, + manual_t2::key_compare + > + > manual_t3; + + compare_structures3<indexed_t,manual_t1,manual_t2,manual_t3>( + "2 ordered indices + 1 sequenced index"); + } + + return 0; +} |