mirror of
https://github.com/boostorg/msm.git
synced 2026-07-21 13:23:45 +00:00
feat(backmp11): introduce flat_fold as default dispatch strategy
This commit is contained in:
committed by
Christian Granzin
parent
515ff4f8d7
commit
d0cf98bd39
@@ -5,44 +5,37 @@ It is currently in **experimental** stage, thus some details about the compatibi
|
||||
It is named after the metaprogramming library Boost Mp11, the main contributor to the optimizations.
|
||||
Usages of MPL are replaced with Mp11 to get rid of the costly C{plus}{plus}03 emulation of variadic templates.
|
||||
|
||||
The new back-end has the following goals:
|
||||
|
||||
- reduce compilation time and RAM usage
|
||||
- reduce state machine runtime
|
||||
- provide new features and customization options
|
||||
|
||||
It offers a significant reduction in compilation time and RAM usage, as can be seen in these benchmarks:
|
||||
It offers a significant improvement in runtime and memory usage, as can be seen in these benchmarks:
|
||||
|
||||
*Large state machine*
|
||||
|
||||
[width=80%, cols="3,1,1,1", options="header"]
|
||||
|=======================================================================
|
||||
| | Compile / sec | RAM / MB | Runtime / sec
|
||||
| back | 17 | 961 | 2.8
|
||||
| back_favor_compile_time | 22 | 1006 | 3.5
|
||||
| back11 | 40 | 2802 | 2.7
|
||||
| backmp11 | 3 | 218 | 1.1
|
||||
| backmp11_favor_compile_time | 3 | 206 | 6.0
|
||||
| sml | 6 | 273 | 0.5
|
||||
|=======================================================================
|
||||
[%autowidth, options="header"]
|
||||
|=======================================================================================================
|
||||
| | Compile time / sec | Compile RAM / MB | Binary size / kB | Runtime / sec
|
||||
| back | 14 | 815 | 68 | 2.8
|
||||
| back_favor_compile_time | 17 | 775 | 226 | 3.5
|
||||
| back11 | 37 | 2682 | 84 | 2.8
|
||||
| backmp11 | 3 | 209 | 29 | 0.7
|
||||
| backmp11_favor_compile_time | 3 | 193 | 43 | 6.0
|
||||
| sml | 5 | 234 | 57 | 0.3
|
||||
|=======================================================================================================
|
||||
|
||||
|
||||
*Large hierarchical state machine*
|
||||
|
||||
[width=80%, cols="3,1,1,1", options="header"]
|
||||
|================================================================================
|
||||
| | Compile / sec | RAM / MB | Runtime / sec
|
||||
| back | 64 | 2902 | 13.2
|
||||
| back_favor_compile_time | 75 | 2612 | > 300
|
||||
| backmp11 | 9 | 370 | 5.2
|
||||
| backmp11_favor_compile_time | 6 | 282 | 20.3
|
||||
| backmp11_favor_compile_time_multi_cu | 4 | ~917 | 20.7
|
||||
| sml | 24 | 706 | 5.7
|
||||
|================================================================================
|
||||
[%autowidth, options="header"]
|
||||
|================================================================================================================
|
||||
| | Compile time / sec | Compile RAM / MB | Binary size / kB | Runtime / sec
|
||||
| back | 49 | 2165 | 230 | 13.2
|
||||
| back_favor_compile_time | 55 | 1704 | 911 | > 300
|
||||
| backmp11 | 8 | 351 | 85 | 3.3
|
||||
| backmp11_favor_compile_time | 5 | 256 | 100 | 20.4
|
||||
| backmp11_favor_compile_time_multi_cu | 5 | ~863 | 100 | 20.8
|
||||
| sml | 18 | 543 | 422 | 5.4
|
||||
|================================================================================================================
|
||||
|
||||
|
||||
The full code with the benchmarks and more information about them is available https://github.com/chandryan/fsm-benchmarks[in this repository].
|
||||
There are still a couple more optimizations to come, the tables in the repository frequently get updated with the latest results.
|
||||
The full code with the benchmarks and more information about them is available https://github.com/chandryan/fsm-benchmarks[in this repository]. The tables in the repository are frequently updated with results from the latest development branches of the benchmarked libraries.
|
||||
|
||||
|
||||
== Deprecation information
|
||||
@@ -546,57 +539,65 @@ The implementation of these two features depends on mpl_graph, which induces hig
|
||||
Not needed with the functor front-end and was already deprecated, thus removed in `backmp11`.
|
||||
|
||||
|
||||
== How to use it
|
||||
== Compile policies
|
||||
|
||||
The back-end with both its compile policies `favor_runtime_speed` and `favor_compile_time` should be mostly compatible with existing code.
|
||||
Like `back`, this back-end supports 2 compile policies. In case of hierarchical state machines all state machines must be configured with the same compile policy.
|
||||
|
||||
Required replacements to try it out:
|
||||
=== `favor_runtime_speed`
|
||||
|
||||
- for the state machine use `boost::msm::backmp11::state_machine` in place of `boost::msm::back::state_machine` and
|
||||
- for configuring the compile policy and more use `boost::msm::backmp11::state_machine_config`
|
||||
- if you encounter API-incompatibilities please check the <<_other_changes_with_respect_to_back,details above>> for reference
|
||||
This policy favors runtime speed over compile time, it evaluates all transitions and generates the dispatch table at compile time.
|
||||
The dispatch strategy can be tuned by inheriting from `favor_runtime_speed` and adapting the using directive:
|
||||
|
||||
```cpp
|
||||
struct favor_runtime_speed
|
||||
{
|
||||
// Dispatch strategy for processing events.
|
||||
// Supported strategies:
|
||||
// - flat_fold (default)
|
||||
// - function_pointer_array
|
||||
using dispatch_strategy = dispatch_strategy::flat_fold;
|
||||
};
|
||||
```
|
||||
|
||||
It currently supports two dispatch strategies:
|
||||
|
||||
[%autowidth, options="header"]
|
||||
|===
|
||||
| Strategy | Description
|
||||
|
||||
| `flat_fold` (default)
|
||||
| Generates a flat fold of inline comparison branches. +
|
||||
All transition logic is visible to the optimizer. +
|
||||
Usually results in optimal runtime performance and executable size.
|
||||
|
||||
| `function_pointer_array`
|
||||
| Generates an array of function pointers. +
|
||||
Guarantees O(1) dispatch, but the function pointers cannot be inlined. +
|
||||
Usually results in worse runtime performance and larger executable size, but slightly better compile time.
|
||||
|===
|
||||
|
||||
|
||||
Since the back-end should compile very fast for most machines, the manual generation of state machines with the `favor_compile_time` policy has become an opt-in feature.
|
||||
If you want to build your state machine across multiple compilation units, you need to do the following:
|
||||
=== `favor_compile_time`
|
||||
|
||||
- set up a preprocessor define `BOOST_MSM_BACKMP11_MANUAL_GENERATION` before including `msm/backmp11/favor_compile_time.hpp`
|
||||
- then generate your state machine(s) in the compilation units with the macro `BOOST_MSM_BACKMP11_GENERATE_STATE_MACHINE(<smname>)`
|
||||
This policy favors compile time over runtime speed. It evaluates transitions lazily and generates the dispatch table at runtime. Like its counterpart in `back`, it does not support Kleene events.
|
||||
|
||||
Events are wrapped into a `std::any` when they enter event processing to reduce the number of necessary templates instances required to generate the state machine.
|
||||
The policy utilizes a hash map for dispatch, with the type index of each event as the key and an array of function pointers to the matching transitions as the value.
|
||||
|
||||
This policy allows compiling a state machine across multiple translation units (TUs) with the help of a preprocessor macro.
|
||||
Since the back-end should compile very fast for most state machines, this is an opt-in feature.
|
||||
If you want to build your state machine across multiple TUs, you need to do the following:
|
||||
|
||||
- define `BOOST_MSM_BACKMP11_MANUAL_GENERATION` before including `msm/backmp11/favor_compile_time.hpp`
|
||||
- then generate your state machine back-end(s) with the macro `BOOST_MSM_BACKMP11_GENERATE_STATE_MACHINE(<sm_type>)`
|
||||
|
||||
You can find an example for this in the https://github.com/boostorg/msm/blob/develop/test/Backmp11Visitor.cpp[visitor test].
|
||||
|
||||
|
||||
== Applied optimizations
|
||||
== How to use it
|
||||
|
||||
Below you can find some insights how the compile-time and runtime optimizations were achieved.
|
||||
The back-end should be mostly compatible with existing code. Required replacements to try it out:
|
||||
|
||||
- Replacement of CPU-intensive calls (due to C{plus}{plus}03 recursion from MPL) with Mp11
|
||||
- Replaced O(N) algorithms with O(1) alternatives (using additional dispatch tables)
|
||||
- Added more type filters prior to template instantiations
|
||||
- Applied type punning where useful (to reduce template instantiations, e.g. `std::deque` & other things around the dispatch table)
|
||||
|
||||
|
||||
=== `favor_runtime_speed` policy
|
||||
|
||||
Summary:
|
||||
|
||||
- Unconditionally default-initialized everything first and afterwards only the row-related transition cells
|
||||
- Optimized cell initialization with initializer arrays (to reduce template instantiations)
|
||||
|
||||
|
||||
=== `favor_compile_time` policy
|
||||
|
||||
Once an event is given to the FSM for processing, it is immediately wrapped with `std::any` and processing continues with this `any` event.
|
||||
The structure of the dispatch table has been reworked, one dispatch table is created per state as a hash map.
|
||||
The state dispatch tables are designed to directly work with the `any` event, they use the event's type index via its `type()` function as hash value.
|
||||
|
||||
This mechanism enables SMs to forward events to sub-SMs without requiring additional template instantiations just for forwarding as was needed with the `process_any_event` mechanism.
|
||||
The new mechanism enables **forwarding of events to sub-SMs in O(1) order instead of O(N)**.
|
||||
|
||||
Summary:
|
||||
|
||||
- Use one dispatch table per state to reduce compiler processing time
|
||||
- The algorithms for processing the STT and states are optimized to go through rows and states only once
|
||||
- These dispatch tables are hash tables with type_id as key
|
||||
- Apply type erasure with `std::any` as early as possible and do further processing only with any events
|
||||
- each dispatch table only has to cover the events it's handling, no template instantiations required for forwarding events to sub-SMs
|
||||
- for the state machine use `boost::msm::backmp11::state_machine` in place of `boost::msm::back::state_machine` and
|
||||
- for configuring the compile policy and more use `boost::msm::backmp11::state_machine_config`
|
||||
- if you encounter API-incompatibilities please check the <<_other_changes_with_respect_to_back,details above>> for reference
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* fix(backmp11): Completion events fire too often (https://github.com/boostorg/msm/issues/166[#166])
|
||||
* feat(backmp11): Small Object Optimization for events in the event pool (https://github.com/boostorg/msm/issues/172[#172])
|
||||
* feat(backmp11): Improve support for the `deferred_events` property in hierarchical state machines (https://github.com/boostorg/msm/issues/173[#173])
|
||||
* feat(backmp11): Improve runtime performance with a `flat_fold` dispatch strategy (https://github.com/boostorg/msm/issues/180[#180])
|
||||
|
||||
== Boost 1.90
|
||||
|
||||
|
||||
@@ -182,6 +182,9 @@ class deferred_event : public event_occurrence
|
||||
Event m_event;
|
||||
};
|
||||
|
||||
template <typename Policy, typename = void>
|
||||
struct compile_policy_impl;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace boost::msm::backmp11
|
||||
|
||||
|
||||
@@ -21,15 +21,22 @@
|
||||
namespace boost { namespace msm { namespace backmp11
|
||||
{
|
||||
|
||||
struct favor_runtime_speed {};
|
||||
struct favor_runtime_speed
|
||||
{
|
||||
// Dispatch strategy for processing events.
|
||||
// Supported strategies:
|
||||
// - flat_fold (default)
|
||||
// - function_pointer_array
|
||||
using dispatch_strategy = dispatch_strategy::flat_fold;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Policy>
|
||||
struct compile_policy_impl;
|
||||
template <>
|
||||
struct compile_policy_impl<favor_runtime_speed>
|
||||
struct compile_policy_impl<
|
||||
Policy,
|
||||
std::enable_if_t<std::is_base_of_v<favor_runtime_speed, Policy>>>
|
||||
{
|
||||
template <typename Event>
|
||||
constexpr static const Event& normalize_event(const Event& event)
|
||||
@@ -147,8 +154,8 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
if constexpr (has_transitions::value ||
|
||||
has_forward_transitions::value)
|
||||
{
|
||||
const dispatch_table_impl& self = dispatch_table_impl::instance();
|
||||
return self.dispatch(sm, region_id, event);
|
||||
using table = dispatch_impl<typename Policy::dispatch_strategy>;
|
||||
return table::dispatch(sm, region_id, event);
|
||||
}
|
||||
return process_result::HANDLED_FALSE;
|
||||
}
|
||||
@@ -235,61 +242,8 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
using has_forward_transitions =
|
||||
mp11::mp_not<mp11::mp_empty<submachines_with_forward_transitions>>;
|
||||
|
||||
class dispatch_table_impl
|
||||
class dispatch_base
|
||||
{
|
||||
public:
|
||||
static const dispatch_table_impl& instance() {
|
||||
static dispatch_table_impl table;
|
||||
return table;
|
||||
}
|
||||
|
||||
process_result dispatch(
|
||||
StateMachine& sm, int region_id, const Event& event) const
|
||||
{
|
||||
const int state_id = sm.m_active_state_ids[region_id];
|
||||
const cell_t& cell = m_cells[state_id];
|
||||
if (cell)
|
||||
{
|
||||
return cell(sm, region_id, event);
|
||||
}
|
||||
return process_result::HANDLED_FALSE;
|
||||
}
|
||||
|
||||
private:
|
||||
dispatch_table_impl()
|
||||
{
|
||||
using initial_map_items = mp11::mp_transform<
|
||||
initial_map_item,
|
||||
submachines_with_forward_transitions>;
|
||||
using filtered_transitions_by_state_map = mp11::mp_fold<
|
||||
filtered_transition_table,
|
||||
initial_map_items,
|
||||
map_updater>;
|
||||
using merged_transitions = mp11::mp_transform<
|
||||
merge_transitions,
|
||||
filtered_transitions_by_state_map>;
|
||||
using init_cell_constants = mp11::mp_transform<
|
||||
get_init_cell_constant,
|
||||
merged_transitions>;
|
||||
dispatch_table_initializer::execute(
|
||||
reinterpret_cast<generic_cell*>(m_cells),
|
||||
reinterpret_cast<const generic_init_cell_value*>(
|
||||
value_array<init_cell_constants>),
|
||||
mp11::mp_size<init_cell_constants>::value);
|
||||
}
|
||||
|
||||
template <typename Transition>
|
||||
static process_result convert_event_and_execute(StateMachine& sm,
|
||||
int region_id,
|
||||
Event const& evt)
|
||||
{
|
||||
typename Transition::transition_event kleene_event{evt};
|
||||
return Transition::execute(sm, region_id, kleene_event);
|
||||
}
|
||||
|
||||
template <size_t index, cell_t cell>
|
||||
using init_cell_constant = init_cell_constant<index, cell_t, cell>;
|
||||
|
||||
// Build a map with key=state/value=[matching_transitions]
|
||||
// from the filtered transition table.
|
||||
template <typename M, typename Key, typename Value>
|
||||
@@ -326,7 +280,6 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
template <typename Map, typename Transition>
|
||||
using map_updater = typename map_updater_impl<Map, Transition>::type;
|
||||
|
||||
private:
|
||||
// Pseudo-transition used to forward event processing to a submachine.
|
||||
template <typename Submachine>
|
||||
struct forward_transition
|
||||
@@ -375,34 +328,174 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
mp11::mp_first<StateAndTransitions>,
|
||||
mp11::mp_second<StateAndTransitions>>::type;
|
||||
|
||||
// Convert the merged transitions into
|
||||
// initializer cell constants for the dispatch table.
|
||||
using initial_map_items =
|
||||
mp11::mp_transform<initial_map_item,
|
||||
submachines_with_forward_transitions>;
|
||||
using filtered_transitions_by_state_map =
|
||||
mp11::mp_fold<filtered_transition_table,
|
||||
initial_map_items,
|
||||
map_updater>;
|
||||
|
||||
public:
|
||||
using merged_transitions =
|
||||
mp11::mp_transform<merge_transitions,
|
||||
filtered_transitions_by_state_map>;
|
||||
|
||||
template <typename Transition>
|
||||
static process_result convert_event_and_execute(StateMachine& sm,
|
||||
int region_id,
|
||||
Event const& evt)
|
||||
{
|
||||
typename Transition::transition_event kleene_event{evt};
|
||||
return Transition::execute(sm, region_id, kleene_event);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Strategy, typename NotExplicit = void>
|
||||
class dispatch_impl;
|
||||
|
||||
template <typename NotExplicit>
|
||||
class dispatch_impl<dispatch_strategy::flat_fold, NotExplicit>
|
||||
: public dispatch_base
|
||||
{
|
||||
using base = dispatch_base;
|
||||
public:
|
||||
template <typename... Ts>
|
||||
struct mp_indexed_dispatch_impl
|
||||
{
|
||||
using list = mp11::mp_list<Ts...>;
|
||||
static constexpr std::size_t size = sizeof...(Ts);
|
||||
|
||||
template <typename F>
|
||||
static constexpr process_result invoke(int state_id, F&& func)
|
||||
{
|
||||
return dispatch(state_id, func,
|
||||
std::make_index_sequence<size>{});
|
||||
}
|
||||
|
||||
private:
|
||||
// For position I in the list, get the corresponding state_id
|
||||
// at compile time.
|
||||
template <std::size_t I>
|
||||
static constexpr int state_id_at =
|
||||
StateMachine::template get_state_id<
|
||||
typename mp11::mp_at_c<list, I>::current_state_type>();
|
||||
|
||||
// Single case handler: invoke func with the I-th transition
|
||||
template <std::size_t I, typename F>
|
||||
static constexpr process_result handle_case(F& func)
|
||||
{
|
||||
return func(mp11::mp_at_c<list, I>{});
|
||||
}
|
||||
|
||||
// Generate branches comparing state_id against compile-time
|
||||
// state_id_at<I> for each position I in the list.
|
||||
template <typename F, std::size_t... Is>
|
||||
static constexpr process_result dispatch(int state_id, F& func,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
process_result result = process_result::HANDLED_FALSE;
|
||||
// Each branch compares the runtime state_id against the
|
||||
// compile-time state_id of the I-th transition.
|
||||
// State IDs may be non-contiguous (e.g., 0, 2, 5).
|
||||
(void)((state_id == state_id_at<Is> &&
|
||||
(result = handle_case<Is>(func), true)) || ...);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename F>
|
||||
static constexpr process_result mp_indexed_dispatch(int state_id, F&& func)
|
||||
{
|
||||
return mp11::mp_apply<mp_indexed_dispatch_impl, L>::invoke(
|
||||
state_id, std::forward<F>(func));
|
||||
}
|
||||
|
||||
static inline process_result dispatch(StateMachine& sm, int region_id,
|
||||
const Event& event)
|
||||
{
|
||||
const int state_id = sm.m_active_state_ids[region_id];
|
||||
return mp_indexed_dispatch<typename base::merged_transitions>(state_id,
|
||||
[&sm, region_id, &event](auto transition) -> process_result
|
||||
{
|
||||
using Transition = decltype(transition);
|
||||
using TransitionEvent =
|
||||
typename Transition::transition_event;
|
||||
if constexpr (!is_kleene_event<TransitionEvent>::value)
|
||||
{
|
||||
return Transition::execute(sm, region_id, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
return base::template
|
||||
convert_event_and_execute<Transition>(
|
||||
sm, region_id, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NotExplicit>
|
||||
class dispatch_impl<dispatch_strategy::function_pointer_array, NotExplicit>
|
||||
: public dispatch_base
|
||||
{
|
||||
using base = dispatch_base;
|
||||
public:
|
||||
static process_result dispatch(
|
||||
StateMachine& sm, int region_id, const Event& event)
|
||||
{
|
||||
const int state_id = sm.m_active_state_ids[region_id];
|
||||
constexpr auto& cells = m_cells;
|
||||
const cell_t cell = cells[state_id];
|
||||
if (cell)
|
||||
{
|
||||
return cell(sm, region_id, event);
|
||||
}
|
||||
return process_result::HANDLED_FALSE;
|
||||
}
|
||||
|
||||
private:
|
||||
// Convert a transition to its function pointer.
|
||||
template <typename Transition,
|
||||
bool IsKleeneEvent = is_kleene_event<
|
||||
typename Transition::transition_event>::value>
|
||||
struct get_init_cell_constant_impl;
|
||||
struct get_cell;
|
||||
template <typename Transition>
|
||||
struct get_init_cell_constant_impl<Transition, /*IsKleeneEvent=*/false>
|
||||
struct get_cell<Transition, /*IsKleeneEvent=*/false>
|
||||
{
|
||||
using type = init_cell_constant<
|
||||
// Offset into the entries array
|
||||
StateMachine::template get_state_id<typename Transition::current_state_type>(),
|
||||
// Address of the execute function
|
||||
&Transition::execute>;
|
||||
static constexpr cell_t value = &Transition::execute;
|
||||
};
|
||||
template <typename Transition>
|
||||
struct get_init_cell_constant_impl<Transition, /*IsKleeneEvent=*/true>
|
||||
struct get_cell<Transition, /*IsKleeneEvent=*/true>
|
||||
{
|
||||
using type = init_cell_constant<
|
||||
// Offset into the entries array
|
||||
StateMachine::template get_state_id<typename Transition::current_state_type>(),
|
||||
// Address of the execute function
|
||||
&convert_event_and_execute<Transition>>;
|
||||
static constexpr cell_t value =
|
||||
&base::template convert_event_and_execute<Transition>;
|
||||
};
|
||||
template <typename Transition>
|
||||
using get_init_cell_constant = typename get_init_cell_constant_impl<Transition>::type;
|
||||
static constexpr cell_t cell_v = get_cell<Transition>::value;
|
||||
|
||||
cell_t m_cells[max_state]{};
|
||||
struct cell_table
|
||||
{
|
||||
cell_t data[max_state]{};
|
||||
constexpr cell_t operator[](int i) const { return data[i]; }
|
||||
};
|
||||
|
||||
// Build the cell table by traversing merged_transitions exactly once.
|
||||
// Each transition knows its own state_id, so we just assign directly.
|
||||
template <typename... Transitions>
|
||||
static constexpr cell_table make_cells_from(mp11::mp_list<Transitions...>)
|
||||
{
|
||||
cell_table table{};
|
||||
// Fold expression: one assignment per transition, no searching.
|
||||
((table.data[
|
||||
StateMachine::template get_state_id<
|
||||
typename Transitions::current_state_type>()] =
|
||||
get_cell<Transitions>::value), ...);
|
||||
return table;
|
||||
}
|
||||
|
||||
static constexpr cell_table m_cells =
|
||||
make_cells_from(typename base::merged_transitions{});
|
||||
};
|
||||
|
||||
// "Dispatch" for a specific event (sm-internal).
|
||||
|
||||
@@ -51,8 +51,6 @@ struct favor_compile_time
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Policy>
|
||||
struct compile_policy_impl;
|
||||
template <>
|
||||
struct compile_policy_impl<favor_compile_time>
|
||||
{
|
||||
|
||||
@@ -207,7 +207,7 @@ class state_machine_base : public FrontEnd
|
||||
template <typename StateMachine>
|
||||
friend struct transition_table_impl;
|
||||
|
||||
template <typename Policy>
|
||||
template <typename Policy, typename>
|
||||
friend struct detail::compile_policy_impl;
|
||||
|
||||
template <typename, typename, visit_mode, bool,
|
||||
@@ -916,7 +916,7 @@ class state_machine_base : public FrontEnd
|
||||
}
|
||||
|
||||
// Consider anything except "only deferred" to be a processed event.
|
||||
if (result != process_result::HANDLED_DEFERRED)
|
||||
if (*result != process_result::HANDLED_DEFERRED)
|
||||
{
|
||||
processed_events++;
|
||||
if (processed_events == max_events)
|
||||
|
||||
@@ -25,7 +25,7 @@ struct config_tag {};
|
||||
template <class T>
|
||||
using is_config = std::is_same<typename T::internal::tag, config_tag>;
|
||||
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// Config for the default compile policy
|
||||
// (runtime over compile time).
|
||||
@@ -80,6 +80,26 @@ struct default_state_machine_config
|
||||
|
||||
using state_machine_config = default_state_machine_config;
|
||||
|
||||
} // boost::msm::backmp11
|
||||
// Configuration parameters to select how events are dispatched.
|
||||
namespace dispatch_strategy
|
||||
{
|
||||
|
||||
// Generates a flat fold of inline comparison branches.
|
||||
// The code can be optimized to a jump table.
|
||||
// + Best executable size and runtime speed for most compilers.
|
||||
// + No indirection — fully inlinable.
|
||||
// - O(n) comparisons in the worst case.
|
||||
struct flat_fold {};
|
||||
|
||||
// Generates an array of function pointers.
|
||||
// + O(1) dispatch.
|
||||
// + Slightly better compile times.
|
||||
// - Indirect call through function pointer — not inlinable.
|
||||
// - Larger executable size (one pointer per state per event type).
|
||||
struct function_pointer_array {};
|
||||
|
||||
} // namespace dispatch_strategy
|
||||
|
||||
} // namespace boost::msm::backmp11
|
||||
|
||||
#endif // BOOST_MSM_BACKMP11_STATE_MACHINE_CONFIG_H
|
||||
|
||||
Reference in New Issue
Block a user