libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
msm_performance.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2015 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "stdafx.h"
20 
21 #define BOOST_TEST_MODULE libjmmcg_tests
22 #include <boost/test/included/unit_test.hpp>
23 #include <boost/mpl/size.hpp>
24 
25 #include "core/msm.hpp"
26 
27 #include "core/ave_deviation_meter.hpp"
28 #include "core/stats_output.hpp"
29 
30 #include <chrono>
31 #include <random>
32 
33 using namespace libjmmcg;
34 
35 using timed_results_t=ave_deviation_meter<unsigned long long>;
36 
37 /**
38  Try to ensure that the events are sufficiently complex to baffle the optimizer.
39  */
40 std::mt19937_64 gen(42);
41 std::uniform_int_distribution<unsigned> distribution;
42 auto generator=std::bind(distribution, gen);
43 
44 enum class states {
45  start,
46  assign,
47  assign1,
48  assign2,
49  assign3,
50  assign4,
51  assign5,
52  assign6,
53  assign7,
54  assign8,
55  assign9,
56  end,
57  unknown
58 };
59 
60 std::ostream &
61 operator<<(std::ostream &os, states const s) noexcept(false) {
62  os<<static_cast<long>(s);
63  return os;
64 }
65 
66 struct data {
67  unsigned i{0};
68 };
69 
70 struct assign_event {
71  using argument_type=data;
72 
73  template<auto state, auto next, class Params>
74  void operator()(Params &p) const noexcept(true) {
75  p.i+=generator();
76  }
77 };
78 struct assign_event1 {
79  using argument_type=data;
80 
81  template<auto state, auto next, class Params>
82  void operator()(Params &p) const noexcept(true) {
83  p.i+=generator();
84  }
85 };
86 struct assign_event2 {
87  using argument_type=data;
88 
89  template<auto state, auto next, class Params>
90  void operator()(Params &p) const noexcept(true) {
91  p.i+=generator();
92  }
93 };
94 struct assign_event3 {
95  using argument_type=data;
96 
97  template<auto state, auto next, class Params>
98  void operator()(Params &p) const noexcept(true) {
99  p.i+=generator();
100  }
101 };
103  using argument_type=data;
104 
105  template<auto state, auto next, class Params>
106  void operator()(Params &p) const noexcept(true) {
107  p.i+=generator();
108  }
109 };
111  using argument_type=data;
112 
113  template<auto state, auto next, class Params>
114  void operator()(Params &p) const noexcept(true) {
115  p.i+=generator();
116  }
117 };
119  using argument_type=data;
120 
121  template<auto state, auto next, class Params>
122  void operator()(Params &p) const noexcept(true) {
123  p.i+=generator();
124  }
125 };
127  using argument_type=data;
128 
129  template<auto state, auto next, class Params>
130  void operator()(Params &p) const noexcept(true) {
131  p.i+=generator();
132  }
133 };
135  using argument_type=data;
136 
137  template<auto state, auto next, class Params>
138  void operator()(Params &p) const noexcept(true) {
139  p.i+=generator();
140  }
141 };
143  using argument_type=data;
144 
145  template<auto state, auto next, class Params>
146  void operator()(Params &p) const noexcept(true) {
147  p.i+=generator();
148  }
149 };
150 
151 struct unroll {
152  struct state_machine_t final : msm::unroll::state_transition_table<state_machine_t> {
153  using row_t=msm::unroll::row_types<states, states>;
154  using transition_table=rows<
155  row_t::row<states::start, assign_event, states::end>,
156  row_t::row<states::assign, assign_event, states::end>,
157  row_t::row<states::assign1, assign_event1, states::end>,
158  row_t::row<states::assign2, assign_event2, states::end>,
159  row_t::row<states::assign3, assign_event3, states::end>,
160  row_t::row<states::assign4, assign_event4, states::end>,
161  row_t::row<states::assign5, assign_event5, states::end>,
162  row_t::row<states::assign6, assign_event6, states::end>,
163  row_t::row<states::assign7, assign_event7, states::end>,
164  row_t::row<states::assign8, assign_event8, states::end>,
165  row_t::row<states::assign9, assign_event9, states::end>
166  >;
167  };
168  using machine=msm::unroll::machine<state_machine_t>;
169 
170  template<class Arg>
171  void process(states state, Arg &p) noexcept(false) {
172  msm.process(state, p);
173  }
174 
175  machine msm;
176 };
177 
178 struct jump_table {
179  struct state_machine_t final : msm::jump_table::state_transition_table<state_machine_t> {
181  using transition_table=rows<
182  row_t::row<states::start, assign_event, states::end>,
183  row_t::row<states::assign, assign_event, states::end>,
184  row_t::row<states::assign1, assign_event1, states::end>,
185  row_t::row<states::assign2, assign_event2, states::end>,
186  row_t::row<states::assign3, assign_event3, states::end>,
187  row_t::row<states::assign4, assign_event4, states::end>,
188  row_t::row<states::assign5, assign_event5, states::end>,
189  row_t::row<states::assign6, assign_event6, states::end>,
190  row_t::row<states::assign7, assign_event7, states::end>,
191  row_t::row<states::assign8, assign_event8, states::end>,
192  row_t::row<states::assign9, assign_event9, states::end>
193  >;
194  };
195  using machine=msm::jump_table::machine<state_machine_t>;
196 
197  template<class Arg>
198  void process(states state, Arg &p) noexcept(false) {
199  msm.process(state, p);
200  }
201 
202  machine msm;
203 };
204 
206  struct state_machine_t final : msm::computed_goto::state_transition_table<state_machine_t> {
208  using transition_table=rows<
209  row_t::row<states::start, assign_event, states::end>,
210  row_t::row<states::assign, assign_event, states::end>,
211  row_t::row<states::assign1, assign_event1, states::end>,
212  row_t::row<states::assign2, assign_event2, states::end>,
213  row_t::row<states::assign3, assign_event3, states::end>,
214  row_t::row<states::assign4, assign_event4, states::end>,
215  row_t::row<states::assign5, assign_event5, states::end>,
216  row_t::row<states::assign6, assign_event6, states::end>,
217  row_t::row<states::assign7, assign_event7, states::end>,
218  row_t::row<states::assign8, assign_event8, states::end>,
219  row_t::row<states::assign9, assign_event9, states::end>
220  >;
221  };
222  using machine=msm::computed_goto::machine<state_machine_t>;
223 
224  template<class Arg>
225  void process(states state, Arg &p) noexcept(false) {
226  msm.process(state, p);
227  }
228 
229  machine msm;
230 };
231 
232 struct hash {
233  struct state_machine_t final : msm::hash::state_transition_table<state_machine_t> {
234  using row_t=msm::hash::row_types<states, states>;
235  using transition_table=rows<
236  row_t::row<states::start, assign_event, states::end>,
237  row_t::row<states::assign, assign_event, states::end>,
238  row_t::row<states::assign1, assign_event1, states::end>,
239  row_t::row<states::assign2, assign_event2, states::end>,
240  row_t::row<states::assign3, assign_event3, states::end>,
241  row_t::row<states::assign4, assign_event4, states::end>,
242  row_t::row<states::assign5, assign_event5, states::end>,
243  row_t::row<states::assign6, assign_event6, states::end>,
244  row_t::row<states::assign7, assign_event7, states::end>,
245  row_t::row<states::assign8, assign_event8, states::end>,
246  row_t::row<states::assign9, assign_event9, states::end>
247  >;
248  };
249  using machine=msm::hash::machine<state_machine_t>;
250 
251  template<class Arg>
252  void process(states state, Arg &p) noexcept(false) {
253  msm.process(state, p);
254  }
255 
256  machine msm;
257 };
258 
259 using state_machines_t=boost::mpl::list<
260  unroll::machine,
261  jump_table::machine/*,
262 // TODO computed_goto::machine,
263  hash::machine*/
264 >;
265 
266 BOOST_AUTO_TEST_SUITE(msm_tests)
267 
268 BOOST_AUTO_TEST_SUITE(performance, *stats_to_csv::make_fixture("msm_performance.csv"))
269 
270 /**
271  \test <a href="./examples/msm_performance.svg">Graph</a> of the performance comparison of various implementations of an msm.
272  ==============================================================================
273  Performance of randomly-called state-changes per second.
274 */
275 BOOST_AUTO_TEST_CASE_TEMPLATE(state_changes, state_machine_t, state_machines_t) {
276  const unsigned short loops_for_conv=50;
277  const double perc_conv_estimate=5.0;
278 #ifdef JMMCG_PERFORMANCE_TESTS
279  const unsigned long test_size=10000000U;
280 #else
281  const unsigned long test_size=10U;
282 #endif
283 
284  const std::pair<timed_results_t, bool> timed_results(compute_average_deviation<timed_results_t::value_type>(
285  perc_conv_estimate,
286  loops_for_conv,
287  []() {
288  state_machine_t msm;
289  data d;
290  std::mt19937_64 gen(42);
291  std::uniform_int_distribution<unsigned> distribution(static_cast<unsigned>(states::start), static_cast<unsigned>(states::end)-1);
292  auto generator=std::bind(distribution, gen);
293  const auto t1=std::chrono::high_resolution_clock::now();
294  for (unsigned long num_loops=0;num_loops<test_size;++num_loops) {
295  msm.process(static_cast<states>(generator()), d);
296  }
297  const auto t2=std::chrono::high_resolution_clock::now();
298  BOOST_CHECK_GT(d.i, 0);
299  return timed_results_t::value_type(test_size/(static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count())/1000000000));
300  }
301  ));
302  std::cout<<typeid(state_machine_t).name()<<" state-changes/sec="<<timed_results.first<<std::endl;
303 #ifdef JMMCG_PERFORMANCE_TESTS
304  stats_to_csv::handle->stats<<timed_results.first.to_csv()<<std::flush;
305  BOOST_CHECK(!timed_results.second);
306 #endif
307 }
308 
309 BOOST_AUTO_TEST_SUITE_END()
310 
311 BOOST_AUTO_TEST_SUITE_END()