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