// Synthetic speed comparison of "virtual", "switch", "if-else O(N)", "if-else O(logN)" and "static". // Tests are done for different "types" count (i.e. "case branches" or count of derived classes, etc). // NOTE: try different settings: MAX_TYPES_COUNT, N, try to play with compiler optimization options, // process affinity mask, etc // NOTE: Calculations are simple, in "static" case they can be very efficently optimized (folded). // Author: Evgeny Panasyuk #include <algorithm> #include <iostream> #include <iomanip> #include <vector> #include <ios> #include <boost/ptr_container/ptr_vector.hpp> #include <boost/preprocessor/repetition.hpp> #include <boost/range/iterator_range.hpp> #include <boost/mpl/for_each.hpp> #include <boost/mpl/list.hpp> #include <boost/config/suffix.hpp> #include <boost/timer.hpp> #include <boost/foreach.hpp> template<int types_cnt> struct dispatch_base_shuffle_indexed; template<int types_cnt> struct dispatch_base_regular; //------------------------------Settings--------------------------------- #define IDEONE_DOT_COM // Comment this line - it is only for ideone.com // Max types count. Tests will be done for 1 to MAX_TYPES_COUNT types #define MAX_TYPES_COUNT 128 // Work amount for one test const int N=100000;//100000 // Outter loop iterations const int OUTER_ITERS=16; template<int T> struct dispatch_base { // Select one of indexing type below: typedef dispatch_base_shuffle_indexed<T> type; //typedef dispatch_base_regular<T> type; }; //------------------------------Common stuff----------------------------- #ifdef IDEONE_DOT_COM #undef BOOST_FORCEINLINE #define BOOST_FORCEINLINE inline __attribute__ ((always_inline)) #undef MAX_TYPES_COUNT #define MAX_TYPES_COUNT 64 #else #include <boost/range/algorithm/random_shuffle.hpp> #endif struct dispatch_data { double elapsed; int result; const char *name; }; template<class T,int types_cnt,int iters_cnt=(MAX_TYPES_COUNT*N/types_cnt)> struct dispatch: public T { void do_test() { data.result=0; this->init(); boost::timer t; for (int i = 0; i !=iters_cnt; ++i) { data.result+=this->iter(i); } data.elapsed = t.elapsed(); data.name=this->get_name(); } dispatch_data data; static const int types_count=types_cnt; static const int iterations_cnt=iters_cnt; }; template<template<int>class T,int types_cnt> struct dispatch_m { typedef dispatch<T<types_cnt>,types_cnt> type; }; template<int types_cnt> struct dispatch_base_regular { BOOST_FORCEINLINE int get_type_t(int i) { return i; } }; template<int types_cnt> struct dispatch_base_shuffle_indexed { typedef std::vector<int> index_vector_t; dispatch_base_shuffle_indexed(): current_i(0) { for(int d=0;d!=deck_count;++d) { for(int i=0;i!=types_cnt;++i) { index.push_back(i); } #ifndef IDEONE_DOT_COM boost::random_shuffle(boost::iterator_range<index_vector_t::iterator>(index.begin()+d*types_cnt,index.begin()+(d+1)*types_cnt)); #else std::random_shuffle(index.begin()+d*types_cnt,index.begin()+(d+1)*types_cnt); #endif } } BOOST_FORCEINLINE int get_type_t(int i) { return index[(current_i++)%index.size()]; } private: static const int deck_count=128; index_vector_t index; int current_i; }; template<int types_cnt,template<int,int>class T> struct branch_dispatch_base: protected dispatch_base<types_cnt>::type { protected: std::vector<int> v; public: void init() { for (int i = 0; i != types_cnt; ++i) { v.push_back(i); } } BOOST_FORCEINLINE int iter(int i) { int result=0; for(int t=0;t!=types_cnt;++t) { result+=T<types_cnt,types_cnt>::call(v[this->get_type_t(t)],i); } return result; } }; #define CALC (x+p) //------------------------Stuff for "switch" test------------------------ template<int p> BOOST_FORCEINLINE int func(int x) { return CALC; }; #define CASE_MACRO(z, p, unused) case p:return func<p>(i); template<int p,int> struct test_switch; #define SWITCH_FUNCS(z, p, unused) \ template<int types_cnt> \ struct test_switch<p+1,types_cnt> \ { \ BOOST_FORCEINLINE static int call(int x,int i) \ { \ switch(x) \ { \ BOOST_PP_REPEAT(BOOST_PP_ADD(p,1), CASE_MACRO, ~) \ default:return -1; \ } \ } \ }; BOOST_PP_REPEAT(MAX_TYPES_COUNT, SWITCH_FUNCS, ~) template<int types_cnt> struct switch_dispatch: public branch_dispatch_base<types_cnt,test_switch> { static const char * const get_name() { return "switch"; }; }; //----------------------------Stuff for "if-else O(N)"------------------- template<int p,int types_cnt> struct test_ifelse_on { BOOST_FORCEINLINE static int call(int x,int i) { if(x==(types_cnt-p)) return func<types_cnt-p>(i); return test_ifelse_on<p-1,types_cnt>::call(x,i); } }; template<int types_cnt> struct test_ifelse_on<0,types_cnt> { BOOST_FORCEINLINE static int call(int x,int i) { return -1; } }; template<int types_cnt> struct ifelse_on_dispatch: public branch_dispatch_base<types_cnt,test_ifelse_on> { static const char * const get_name() { return "if-else_O(N)"; }; }; //----------------------Stuff for "if-else O(logN)"---------------------- template<int lower,int upper/*,int level*/> struct test_ifelse_ologn { BOOST_FORCEINLINE static int call(int x,int i) { const int middle=(lower+upper)/2; if(x>middle) return test_ifelse_ologn<middle+1,upper/*,level-1*/>::call(x,i); else return test_ifelse_ologn<lower,middle/*,level-1*/>::call(x,i); } }; template<int lower> struct test_ifelse_ologn<lower,lower> { BOOST_FORCEINLINE static int call(int x,int i) { return lower+i; } }; template<int types_cnt,int> struct test_ifelse_ologn_m: public test_ifelse_ologn<0,types_cnt-1> { }; template<int types_cnt> struct ifelse_ologn_dispatch: public branch_dispatch_base<types_cnt,test_ifelse_ologn_m> { static const char * const get_name() { return "if-else_O(log(N))"; }; }; //---------------------Stuff for "virtual" call test--------------------- struct base { virtual int call(int x)=0; }; template<int p> class derived: public base { virtual int call(int x) { return CALC; }; }; template<int p> struct fill_vector { static void f(boost::ptr_vector<base> &v) { fill_vector<p-1>::f(v); v.push_back(new derived<p-1>()); } }; template<> struct fill_vector<0> { static void f(boost::ptr_vector<base> &v) {} }; template<int types_cnt> struct virtual_dispatch: protected dispatch_base<types_cnt>::type { void init() { fill_vector<types_cnt>::f(v); } BOOST_FORCEINLINE int iter(int i) { int result=0; for(int t=0;t!=types_cnt;++t) { result+=v[this->get_type_t(t)].call(i); } return result; } static const char * const get_name() { return "virtual"; }; private: boost::ptr_vector<base> v; }; //---------------------Stuff for "static" call test---------------------- // It is possible to omit this struct, it just for "emulate" policies // (for cases when compiler can't do good optimizations) template<class T> struct static_call: public T { }; template<int p> struct static_call_policy { static BOOST_FORCEINLINE int static_func(int x) { return static_call_policy<p-1>::static_func(x)+CALC; }; }; template<> struct static_call_policy<0> { static BOOST_FORCEINLINE int static_func(int x) { return x; }; }; template<int types_cnt> struct static_dispatch { void init() {} BOOST_FORCEINLINE int iter(int i) { return static_call< static_call_policy<types_cnt-1> >::static_func(i); } static const char * const get_name() { return "static"; }; }; //-----------------------------Testing stuff----------------------------- template<int types_cnt> struct dispatchers_t { typedef boost::mpl::list < typename dispatch_m<static_dispatch,types_cnt>::type, typename dispatch_m<switch_dispatch,types_cnt>::type, typename dispatch_m<virtual_dispatch,types_cnt>::type, typename dispatch_m<ifelse_ologn_dispatch,types_cnt>::type, typename dispatch_m<ifelse_on_dispatch,types_cnt>::type > type; }; struct test_elapsed { template<class D> void operator()(D &d) { d.do_test(); if((results.size()>0)&&(results[0].result!=d.data.result)) { std::cerr << "results do not match" << std::endl; } std::cout << "\t" << d.data.name << " elapsed=" << (1.0e9*d.data.elapsed/(d.iterations_cnt*d.types_count)) /*<< " result=" << d.data.result*/ << std::endl; BOOST_FOREACH( dispatch_data data, results ) { std::cout << "\t\t" << d.data.name << "/" << data.name << "=" << d.data.elapsed/data.elapsed << "x (elapsed)" << std::endl; } results.push_back(d.data); } std::vector<dispatch_data> results; }; template<int types_cnt> struct do_tests { static void f() { do_tests<types_cnt-1>::f(); std::cout << "Types count=" << types_cnt << std::endl; std::cout << "\tTypes count=" << types_cnt << std::endl; boost::mpl::for_each<typename dispatchers_t<types_cnt>::type>(test_elapsed()); std::cout << std::endl; } }; template<> struct do_tests<0> { static void f() {} }; //----------------------------------------------------------------------- int main(int argc,char **argv) { std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield); //std::cout.precision(3); for(int i=0;i!=OUTER_ITERS;++i) { std::cout << "I=" << i << std::endl; do_tests<MAX_TYPES_COUNT>::f(); } }
Standard input is empty
I=0 Types count=1 Types count=1 static elapsed=0.000000 switch elapsed=20.312500 switch/static=infx (elapsed) virtual elapsed=28.125000 virtual/static=infx (elapsed) virtual/switch=1.384615x (elapsed) if-else_O(log(N)) elapsed=3.125000 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.153846x (elapsed) if-else_O(log(N))/virtual=0.111111x (elapsed) if-else_O(N) elapsed=18.750000 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=0.923077x (elapsed) if-else_O(N)/virtual=0.666667x (elapsed) if-else_O(N)/if-else_O(log(N))=6.000000x (elapsed) Types count=2 Types count=2 static elapsed=1.562500 switch elapsed=21.875000 switch/static=14.000000x (elapsed) virtual elapsed=32.812500 virtual/static=21.000000x (elapsed) virtual/switch=1.500000x (elapsed) if-else_O(log(N)) elapsed=20.312500 if-else_O(log(N))/static=13.000000x (elapsed) if-else_O(log(N))/switch=0.928571x (elapsed) if-else_O(log(N))/virtual=0.619048x (elapsed) if-else_O(N) elapsed=21.875000 if-else_O(N)/static=14.000000x (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.666667x (elapsed) if-else_O(N)/if-else_O(log(N))=1.076923x (elapsed) Types count=3 Types count=3 static elapsed=0.000000 switch elapsed=23.437504 switch/static=infx (elapsed) virtual elapsed=31.250005 virtual/static=infx (elapsed) virtual/switch=1.333333x (elapsed) if-else_O(log(N)) elapsed=21.875003 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.933333x (elapsed) if-else_O(log(N))/virtual=0.700000x (elapsed) if-else_O(N) elapsed=23.437504 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.750000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.071429x (elapsed) Types count=4 Types count=4 static elapsed=0.000000 switch elapsed=23.437500 switch/static=infx (elapsed) virtual elapsed=35.937500 virtual/static=infx (elapsed) virtual/switch=1.533333x (elapsed) if-else_O(log(N)) elapsed=21.875000 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.933333x (elapsed) if-else_O(log(N))/virtual=0.608696x (elapsed) if-else_O(N) elapsed=23.437500 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.652174x (elapsed) if-else_O(N)/if-else_O(log(N))=1.071429x (elapsed) Types count=5 Types count=5 static elapsed=0.000000 switch elapsed=29.687500 switch/static=infx (elapsed) virtual elapsed=34.375000 virtual/static=infx (elapsed) virtual/switch=1.157895x (elapsed) if-else_O(log(N)) elapsed=23.437500 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.789474x (elapsed) if-else_O(log(N))/virtual=0.681818x (elapsed) if-else_O(N) elapsed=25.000000 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=0.842105x (elapsed) if-else_O(N)/virtual=0.727273x (elapsed) if-else_O(N)/if-else_O(log(N))=1.066667x (elapsed) Types count=6 Types count=6 static elapsed=0.000000 switch elapsed=28.125018 switch/static=infx (elapsed) virtual elapsed=35.937522 virtual/static=infx (elapsed) virtual/switch=1.277778x (elapsed) if-else_O(log(N)) elapsed=23.437515 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.833333x (elapsed) if-else_O(log(N))/virtual=0.652174x (elapsed) if-else_O(N) elapsed=28.125018 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.782609x (elapsed) if-else_O(N)/if-else_O(log(N))=1.200000x (elapsed) Types count=7 Types count=7 static elapsed=0.000000 switch elapsed=28.125022 switch/static=infx (elapsed) virtual elapsed=35.937528 virtual/static=infx (elapsed) virtual/switch=1.277778x (elapsed) if-else_O(log(N)) elapsed=25.000020 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.888889x (elapsed) if-else_O(log(N))/virtual=0.695652x (elapsed) if-else_O(N) elapsed=28.125022 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.782609x (elapsed) if-else_O(N)/if-else_O(log(N))=1.125000x (elapsed) Types count=8 Types count=8 static elapsed=0.000000 switch elapsed=29.687500 switch/static=infx (elapsed) virtual elapsed=29.687500 virtual/static=infx (elapsed) virtual/switch=1.000000x (elapsed) if-else_O(log(N)) elapsed=25.000000 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.842105x (elapsed) if-else_O(log(N))/virtual=0.842105x (elapsed) if-else_O(N) elapsed=29.687500 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=1.000000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.187500x (elapsed) Types count=9 Types count=9 static elapsed=0.000000 switch elapsed=28.125004 switch/static=infx (elapsed) virtual elapsed=31.250005 virtual/static=infx (elapsed) virtual/switch=1.111111x (elapsed) if-else_O(log(N)) elapsed=26.562504 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.944444x (elapsed) if-else_O(log(N))/virtual=0.850000x (elapsed) if-else_O(N) elapsed=29.687505 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.055556x (elapsed) if-else_O(N)/virtual=0.950000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.117647x (elapsed) Types count=10 Types count=10 static elapsed=0.000000 switch elapsed=29.687500 switch/static=infx (elapsed) virtual elapsed=31.250000 virtual/static=infx (elapsed) virtual/switch=1.052632x (elapsed) if-else_O(log(N)) elapsed=25.000000 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.842105x (elapsed) if-else_O(log(N))/virtual=0.800000x (elapsed) if-else_O(N) elapsed=29.687500 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.950000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.187500x (elapsed) Types count=11 Types count=11 static elapsed=0.000000 switch elapsed=28.125009 switch/static=infx (elapsed) virtual elapsed=31.250010 virtual/static=infx (elapsed) virtual/switch=1.111111x (elapsed) if-else_O(log(N)) elapsed=26.562508 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.944444x (elapsed) if-else_O(log(N))/virtual=0.850000x (elapsed) if-else_O(N) elapsed=31.250010 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.111111x (elapsed) if-else_O(N)/virtual=1.000000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.176471x (elapsed) Types count=12 Types count=12 static elapsed=0.000000 switch elapsed=28.125018 switch/static=infx (elapsed) virtual elapsed=31.250020 virtual/static=infx (elapsed) virtual/switch=1.111111x (elapsed) if-else_O(log(N)) elapsed=26.562517 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.944444x (elapsed) if-else_O(log(N))/virtual=0.850000x (elapsed) if-else_O(N) elapsed=31.250020 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.111111x (elapsed) if-else_O(N)/virtual=1.000000x (elapsed) if-else_O(N)/if-else_O(log(N))=1.176471x (elapsed) Types count=13 Types count=13 static elapsed=0.000000 switch elapsed=28.125040 switch/static=infx (elapsed) virtual elapsed=32.812546 virtual/static=infx (elapsed) virtual/switch=1.166667x (elapsed) if-else_O(log(N)) elapsed=28.125040 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.000000x (elapsed) if-else_O(log(N))/virtual=0.857143x (elapsed) if-else_O(N) elapsed=31.250044 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.111111x (elapsed) if-else_O(N)/virtual=0.952381x (elapsed) if-else_O(N)/if-else_O(log(N))=1.111111x (elapsed) Types count=14 Types count=14 static elapsed=0.000000 switch elapsed=28.125053 switch/static=infx (elapsed) virtual elapsed=32.812562 virtual/static=infx (elapsed) virtual/switch=1.166667x (elapsed) if-else_O(log(N)) elapsed=28.125053 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.000000x (elapsed) if-else_O(log(N))/virtual=0.857143x (elapsed) if-else_O(N) elapsed=31.250059 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.111111x (elapsed) if-else_O(N)/virtual=0.952381x (elapsed) if-else_O(N)/if-else_O(log(N))=1.111111x (elapsed) Types count=15 Types count=15 static elapsed=0.000000 switch elapsed=31.250049 switch/static=infx (elapsed) virtual elapsed=35.937556 virtual/static=infx (elapsed) virtual/switch=1.150000x (elapsed) if-else_O(log(N)) elapsed=28.125044 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=0.900000x (elapsed) if-else_O(log(N))/virtual=0.782609x (elapsed) if-else_O(N) elapsed=31.250049 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.000000x (elapsed) if-else_O(N)/virtual=0.869565x (elapsed) if-else_O(N)/if-else_O(log(N))=1.111111x (elapsed) Types count=16 Types count=16 static elapsed=0.000000 switch elapsed=29.687500 switch/static=infx (elapsed) virtual elapsed=37.500000 virtual/static=infx (elapsed) virtual/switch=1.263158x (elapsed) if-else_O(log(N)) elapsed=29.687500 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.000000x (elapsed) if-else_O(log(N))/virtual=0.791667x (elapsed) if-else_O(N) elapsed=31.250000 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.052632x (elapsed) if-else_O(N)/virtual=0.833333x (elapsed) if-else_O(N)/if-else_O(log(N))=1.052632x (elapsed) Types count=17 Types count=17 static elapsed=0.000000 switch elapsed=29.687546 switch/static=infx (elapsed) virtual elapsed=37.500059 virtual/static=infx (elapsed) virtual/switch=1.263158x (elapsed) if-else_O(log(N)) elapsed=29.687546 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.000000x (elapsed) if-else_O(log(N))/virtual=0.791667x (elapsed) if-else_O(N) elapsed=31.250049 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.052632x (elapsed) if-else_O(N)/virtual=0.833333x (elapsed) if-else_O(N)/if-else_O(log(N))=1.052632x (elapsed) Types count=18 Types count=18 static elapsed=0.000000 switch elapsed=29.687546 switch/static=infx (elapsed) virtual elapsed=35.937556 virtual/static=infx (elapsed) virtual/switch=1.210526x (elapsed) if-else_O(log(N)) elapsed=29.687546 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.000000x (elapsed) if-else_O(log(N))/virtual=0.826087x (elapsed) if-else_O(N) elapsed=31.250049 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.052632x (elapsed) if-else_O(N)/virtual=0.869565x (elapsed) if-else_O(N)/if-else_O(log(N))=1.052632x (elapsed) Types count=19 Types count=19 static elapsed=0.000000 switch elapsed=29.687509 switch/static=infx (elapsed) virtual elapsed=35.937511 virtual/static=infx (elapsed) virtual/switch=1.210526x (elapsed) if-else_O(log(N)) elapsed=31.250010 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.052632x (elapsed) if-else_O(log(N))/virtual=0.869565x (elapsed) if-else_O(N) elapsed=31.250010 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.052632x (elapsed) if-else_O(N)/virtual=0.869565x (elapsed) if-else_O(N)/if-else_O(log(N))=1.000000x (elapsed) Types count=20 Types count=20 static elapsed=0.000000 switch elapsed=29.687500 switch/static=infx (elapsed) virtual elapsed=35.937500 virtual/static=infx (elapsed) virtual/switch=1.210526x (elapsed) if-else_O(log(N)) elapsed=31.250000 if-else_O(log(N))/static=infx (elapsed) if-else_O(log(N))/switch=1.052632x (elapsed) if-else_O(log(N))/virtual=0.869565x (elapsed) if-else_O(N) elapsed=31.250000 if-else_O(N)/static=infx (elapsed) if-else_O(N)/switch=1.052632x (elapsed) if-else_O(N)/virtual=0.869565x (elapsed) if-else_O(N)/if-else_O(log(N))=1.000000x (elapsed) Types count=21 Types count=21 static elapsed=0.000000