• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2001-2011 Joel de Guzman
3 
4     Distributed under the Boost Software License, Version 1.0. (See accompanying
5     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 ==============================================================================*/
7 #include "measure.hpp"
8 
9 #define FUSION_MAX_LIST_SIZE 30
10 #define FUSION_MAX_VECTOR_SIZE 30
11 
12 #include <boost/fusion/algorithm/iteration/accumulate.hpp>
13 #include <boost/fusion/container/vector.hpp>
14 #include <boost/fusion/container/list.hpp>
15 
16 #include <boost/type_traits/remove_reference.hpp>
17 
18 #include <boost/lexical_cast.hpp>
19 #include <boost/preprocessor/stringize.hpp>
20 #include <boost/preprocessor/enum.hpp>
21 
22 #include <iostream>
23 
24 #ifdef _MSC_VER
25 // inline aggressively
26 # pragma inline_recursion(on) // turn on inline recursion
27 # pragma inline_depth(255)    // max inline depth
28 #endif
29 
30 //  About the tests:
31 //
32 //  The tests below compare various fusion sequences to see how abstraction
33 //  affects prformance.
34 //
35 //  We have 3 sequence sizes for each fusion sequence we're going to test.
36 //
37 //      small = 3 elements
38 //      medium = 10 elements
39 //      big = 30 elements
40 //
41 //  The sequences are initialized with values 0..N-1 from numeric strings
42 //  parsed by boost::lexical_cast to make sure that the compiler is not
43 //  optimizing by replacing the computation with constant results computed
44 //  at compile time.
45 //
46 //  These sequences will be subjected to our accumulator which calls
47 //  fusion::accumulate:
48 //
49 //      this->sum += boost::fusion::accumulate(seq, 0, poly_add());
50 //
51 //  where poly_add simply sums the current value with the content of
52 //  the sequence element. This accumulator will be called many times
53 //  through the "hammer" test (see measure.hpp).
54 //
55 //  The tests are compared against a base using a plain_accumulator
56 //  which does a simple addition:
57 //
58 //      this->sum += x;
59 
60 namespace
61 {
62     struct poly_add
63     {
64         template<typename Sig>
65         struct result;
66 
67         template<typename Lhs, typename Rhs>
68         struct result<poly_add(Lhs, Rhs)>
69             : boost::remove_reference<Lhs>
70         {};
71 
72         template<typename Lhs, typename Rhs>
operator ()__anon8869d5810111::poly_add73         Lhs operator()(const Lhs& lhs, const Rhs& rhs) const
74         {
75             return lhs + rhs;
76         }
77     };
78 
79     // Our Accumulator function
80     template <typename T>
81     struct accumulator
82     {
accumulator__anon8869d5810111::accumulator83         accumulator()
84             : sum()
85         {}
86 
87         template <typename Sequence>
operator ()__anon8869d5810111::accumulator88         void operator()(Sequence const& seq)
89         {
90             this->sum += boost::fusion::accumulate(seq, 0, poly_add());
91         }
92 
93         T sum;
94     };
95 
96     // Plain Accumulator function
97     template <typename T>
98     struct plain_accumulator
99     {
plain_accumulator__anon8869d5810111::plain_accumulator100         plain_accumulator()
101             : sum()
102         {}
103 
104         template <typename X>
operator ()__anon8869d5810111::plain_accumulator105         void operator()(X const& x)
106         {
107             this->sum += x;
108         }
109 
110         T sum;
111     };
112 
113     template <typename T>
check(T const & seq,char const * info)114     void check(T const& seq, char const* info)
115     {
116         test::measure<accumulator<int> >(seq, 1);
117         std::cout << info << test::live_code << std::endl;
118     }
119 
120     template <typename T>
measure(T const & seq,char const * info,long const repeats,double base)121     void measure(T const& seq, char const* info, long const repeats, double base)
122     {
123         double t = test::measure<accumulator<int> >(seq, repeats);
124         std::cout
125             << info
126             << t
127             << " (" << int((t/base)*100) << "%)"
128             << std::endl;
129     }
130 
131     template <typename T>
test_assembler(T const & seq)132     void test_assembler(T const& seq)
133     {
134         test::live_code = boost::fusion::accumulate(seq, 0, poly_add());
135     }
136 }
137 
138 // We'll initialize the sequences from numeric strings that
139 // pass through boost::lexical_cast to make sure that the
140 // compiler is not optimizing by replacing the computation
141 // with constant results computed at compile time.
142 #define INIT(z, n, text) boost::lexical_cast<int>(BOOST_PP_STRINGIZE(n))
143 
main()144 int main()
145 {
146     using namespace boost::fusion;
147     std::cout.setf(std::ios::scientific);
148 
149     vector<
150         int, int, int
151     >
152     vsmall(BOOST_PP_ENUM(3, INIT, _));
153 
154     list<
155         int, int, int
156     >
157     lsmall(BOOST_PP_ENUM(3, INIT, _));
158 
159     vector<
160         int, int, int, int, int, int, int, int, int, int
161     >
162     vmedium(BOOST_PP_ENUM(10, INIT, _));
163 
164     list<
165         int, int, int, int, int, int, int, int, int, int
166     >
167     lmedium(BOOST_PP_ENUM(10, INIT, _));
168 
169     vector<
170         int, int, int, int, int, int, int, int, int, int
171       , int, int, int, int, int, int, int, int, int, int
172       , int, int, int, int, int, int, int, int, int, int
173     >
174     vbig(BOOST_PP_ENUM(30, INIT, _));
175 
176     list<
177         int, int, int, int, int, int, int, int, int, int
178       , int, int, int, int, int, int, int, int, int, int
179       , int, int, int, int, int, int, int, int, int, int
180     >
181     lbig(BOOST_PP_ENUM(30, INIT, _));
182 
183     // first decide how many repetitions to measure
184     long repeats = 100;
185     double measured = 0;
186     while (measured < 2.0 && repeats <= 10000000)
187     {
188         repeats *= 10;
189 
190         boost::timer time;
191 
192         test::hammer<plain_accumulator<int> >(0, repeats);
193         test::hammer<accumulator<int> >(vsmall, repeats);
194         test::hammer<accumulator<int> >(lsmall, repeats);
195         test::hammer<accumulator<int> >(vmedium, repeats);
196         test::hammer<accumulator<int> >(lmedium, repeats);
197         test::hammer<accumulator<int> >(vbig, repeats);
198         test::hammer<accumulator<int> >(lbig, repeats);
199 
200         measured = time.elapsed();
201     }
202 
203     test::measure<plain_accumulator<int> >(1, 1);
204     std::cout
205         << "base accumulated result:            "
206         << test::live_code
207         << std::endl;
208 
209     double base_time = test::measure<plain_accumulator<int> >(1, repeats);
210     std::cout
211         << "base time:                          "
212         << base_time;
213 
214     std::cout
215         << std::endl
216         << "-------------------------------------------------------------------"
217         << std::endl;
218 
219     check(vsmall,       "small vector accumulated result:    ");
220     check(lsmall,       "small list accumulated result:      ");
221     check(vmedium,      "medium vector accumulated result:   ");
222     check(lmedium,      "medium list accumulated result:     ");
223     check(vbig,         "big vector accumulated result:      ");
224     check(lbig,         "big list accumulated result:        ");
225 
226     std::cout
227         << "-------------------------------------------------------------------"
228         << std::endl;
229 
230     measure(vsmall,     "small vector time:                  ", repeats, base_time);
231     measure(lsmall,     "small list time:                    ", repeats, base_time);
232     measure(vmedium,    "medium vector time:                 ", repeats, base_time);
233     measure(lmedium,    "medium list time:                   ", repeats, base_time);
234     measure(vbig,       "big vector time:                    ", repeats, base_time);
235     measure(lbig,       "big list time:                      ", repeats, base_time);
236 
237     std::cout
238         << "-------------------------------------------------------------------"
239         << std::endl;
240 
241     // Let's see how this looks in assembler
242     test_assembler(vmedium);
243 
244     // This is ultimately responsible for preventing all the test code
245     // from being optimized away.  Change this to return 0 and you
246     // unplug the whole test's life support system.
247     return test::live_code != 0;
248 }
249