File: sequence_efficiency.cpp

package info (click to toggle)
boost1.35 1.35.0-5
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 203,856 kB
  • ctags: 337,867
  • sloc: cpp: 938,683; xml: 56,847; ansic: 41,589; python: 18,999; sh: 11,566; makefile: 664; perl: 494; yacc: 456; asm: 353; csh: 6
file content (248 lines) | stat: -rw-r--r-- 7,525 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*=============================================================================
    Copyright (c) 2001-2006 Joel de Guzman

    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)
==============================================================================*/
#include "measure.hpp"

#define FUSION_MAX_LIST_SIZE 30
#define FUSION_MAX_VECTOR_SIZE 30

#include <boost/fusion/algorithm/iteration/accumulate.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/list.hpp>

#include <boost/type_traits/remove_reference.hpp>

#include <boost/lexical_cast.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/enum.hpp>

#include <iostream>

#ifdef _MSC_VER
// inline aggressively
# pragma inline_recursion(on) // turn on inline recursion
# pragma inline_depth(255)    // max inline depth
#endif

//  About the tests:
//
//  The tests below compare various fusion sequences to see how abstraction
//  affects prformance.
//
//  We have 3 sequence sizes for each fusion sequence we're going to test.
//
//      small = 3 elements
//      medium = 10 elements
//      big = 30 elements
//
//  The sequences are initialized with values 0..N-1 from numeric strings
//  parsed by boost::lexical_cast to make sure that the compiler is not 
//  optimizing by replacing the computation with constant results computed 
//  at compile time.
//
//  These sequences will be subjected to our accumulator which calls
//  fusion::accumulate:
//  
//      this->sum += boost::fusion::accumulate(seq, 0, poly_add());
//
//  where poly_add simply sums the current value with the content of
//  the sequence element. This accumulator will be called many times
//  through the "hammer" test (see measure.hpp).
//
//  The tests are compared against a base using a plain_accumulator
//  which does a simple addition:
//
//      this->sum += x;

namespace
{
    struct poly_add
    {
        template<typename Sig>
        struct result;

        template<typename Lhs, typename Rhs>
        struct result<poly_add(Lhs, Rhs)>
            : boost::remove_reference<Lhs>
        {};

        template<typename Lhs, typename Rhs>
        Lhs operator()(const Lhs& lhs, const Rhs& rhs) const
        {
            return lhs + rhs;
        }
    };

    // Our Accumulator function
    template <typename T>
    struct accumulator
    {
        accumulator()
            : sum()
        {}
        
        template <typename Sequence>
        void operator()(Sequence const& seq)
        {
            this->sum += boost::fusion::accumulate(seq, 0, poly_add());
        }
        
        T sum;
    };

    // Plain Accumulator function
    template <typename T>
    struct plain_accumulator
    {
        plain_accumulator()
            : sum()
        {}
        
        template <typename X>
        void operator()(X const& x)
        {
            this->sum += x;
        }
        
        T sum;
    };
    
    template <typename T>
    void check(T const& seq, char const* info)
    {
        test::measure<accumulator<int> >(seq, 1);
        std::cout << info << test::live_code << std::endl;
    }

    template <typename T>
    void measure(T const& seq, char const* info, long const repeats, double base)
    {
        double t = test::measure<accumulator<int> >(seq, repeats);
        std::cout 
            << info
            << t
            << " (" << int((t/base)*100) << "%)"
            << std::endl;
    }

    template <typename T>
    void test_assembler(T const& seq)
    {
        test::live_code = boost::fusion::accumulate(seq, 0, poly_add());
    }
}

// We'll initialize the sequences from numeric strings that
// pass through boost::lexical_cast to make sure that the
// compiler is not optimizing by replacing the computation 
// with constant results computed at compile time.
#define INIT(z, n, text) boost::lexical_cast<int>(BOOST_PP_STRINGIZE(n))

int main()
{
    using namespace boost::fusion;
    std::cout.setf(std::ios::scientific);

    vector<
        int, int, int
    > 
    vsmall(BOOST_PP_ENUM(3, INIT, _));

    list<
        int, int, int
    > 
    lsmall(BOOST_PP_ENUM(3, INIT, _));

    vector<
        int, int, int, int, int, int, int, int, int, int
    > 
    vmedium(BOOST_PP_ENUM(10, INIT, _));

    list<
        int, int, int, int, int, int, int, int, int, int
    > 
    lmedium(BOOST_PP_ENUM(10, INIT, _));

    vector<
        int, int, int, int, int, int, int, int, int, int
      , int, int, int, int, int, int, int, int, int, int
      , int, int, int, int, int, int, int, int, int, int
    > 
    vbig(BOOST_PP_ENUM(30, INIT, _));

    list<
        int, int, int, int, int, int, int, int, int, int
      , int, int, int, int, int, int, int, int, int, int
      , int, int, int, int, int, int, int, int, int, int
    > 
    lbig(BOOST_PP_ENUM(30, INIT, _));

    // first decide how many repetitions to measure
    long repeats = 100;
    double measured = 0;
    while (measured < 2.0 && repeats <= 10000000)
    {
        repeats *= 10;
        
        boost::timer time;

        test::hammer<plain_accumulator<int> >(0, repeats);
        test::hammer<accumulator<int> >(vsmall, repeats);
        test::hammer<accumulator<int> >(lsmall, repeats);
        test::hammer<accumulator<int> >(vmedium, repeats);
        test::hammer<accumulator<int> >(lmedium, repeats);
        test::hammer<accumulator<int> >(vbig, repeats);
        test::hammer<accumulator<int> >(lbig, repeats);

        measured = time.elapsed();
    }

    test::measure<plain_accumulator<int> >(1, 1);
    std::cout 
        << "base accumulated result:            " 
        << test::live_code 
        << std::endl;

    double base_time = test::measure<plain_accumulator<int> >(1, repeats);
    std::cout 
        << "base time:                          "
        << base_time;

    std::cout 
        << std::endl
        << "-------------------------------------------------------------------"
        << std::endl;

    check(vsmall,       "small vector accumulated result:    ");
    check(lsmall,       "small list accumulated result:      ");
    check(vmedium,      "medium vector accumulated result:   ");
    check(lmedium,      "medium list accumulated result:     ");
    check(vbig,         "big vector accumulated result:      "); 
    check(lbig,         "big list accumulated result:        ");

    std::cout 
        << "-------------------------------------------------------------------"
        << std::endl;

    measure(vsmall,     "small vector time:                  ", repeats, base_time);
    measure(lsmall,     "small list time:                    ", repeats, base_time);
    measure(vmedium,    "medium vector time:                 ", repeats, base_time);
    measure(lmedium,    "medium list time:                   ", repeats, base_time);
    measure(vbig,       "big vector time:                    ", repeats, base_time);
    measure(lbig,       "big list time:                      ", repeats, base_time);

    std::cout 
        << "-------------------------------------------------------------------"
        << std::endl;

    // Let's see how this looks in assembler
    test_assembler(vmedium);

    // This is ultimately responsible for preventing all the test code
    // from being optimized away.  Change this to return 0 and you
    // unplug the whole test's life support system.
    return test::live_code != 0;
}