Upload
emboconference
View
697
Download
5
Embed Size (px)
Citation preview
1
emBO++ 2017
'EMBEDDING' A METASTATE MACHINE
Kris Jusiak, Quantlab
| | [email protected] @krisjusiak linkedin.com/in/kris-jusiak
1
2
AGENDAConnection example
ImplementationNaiveEnum/SwitchVariant (C++17)State Machine Language (SML)
[Boost].SMLOverviewSML vs Boost.MSM vs Boost.StatechartDesign in a nutshell
Summary
3 . 1
STORY: CONNECTION (BDD STYLE)Scenario 1: Establishing connection Given the connection is disconnected When the user requests to connect Then the establish request should be sent And the system should wait for estaliblished event
Scenario 2: Keeping connection Given the connection is connected When the valid ping is recieved Then the timeout should be reset
Scenario 3: Connection timeout Given the connection is connected When the timeout is recieved Then the establish should be called And connecting process start again
Scenario 4: Disconnecting Given the connection is connected When the disconnect event is recieved Then the close should be called And user should get disconnected
3 . 2
CONNECTION: STATE DIAGRAM (UML 2.5)
Transition - Uni ed Modeling Language (UML)
3 . 3
CONNECTION - HELPERS/DETAILSvoid resetTimeout() std::puts("resetTimeout"); void establish() std::puts("establish"); void close() std::puts("close"); bool is_valid(const Ping&) return true;
3 . 4
CONNECTION (V1) - NAIVE IMPLEMENTATIONclass ConnectionV1 bool disconnected = true, connected = false, connecting = false;
public: void connect() if (disconnected || connected) establish(); disconnected = false; connecting = true;
void disconnect() if (connecting || connected) close(); connected = false; disconnected = true;
3 . 5
CONNECTION (V1) - NAIVE IMPLEMENTATION void established() connecting = false; connected = true;
void ping(const Ping& event) if (connected && is_valid(event)) resetTimeout();
void timeout() connect(); ;
3 . 6
CONNECTION (V1) - BENCHMARKGCC-7 Clang-3.9
Compilation time 0.101s 0.122s
sizeof(ConnectionV1) 3b 3b
Executable size 6.2K 6.2K
Connect: ASM x86-64
main: sub rsp, 8 mov edi, OFFSET FLAT:.LC1 call puts xor eax, eax add rsp, 8 ret
https://godbolt.org/g/0qpgvv
3 . 7
CONNECTION (V1) - SUMMARY(+) Quick compilation times(+) Good performance(-) Uses more size than required(-) Hard to follow(-) Hard to extend and maintain(-) Hard to test
3 . 8
CONNECTION (V2) - ENUM/SWITCHIMPLEMENTATION
class ConnectionV2 enum class State : unsigned char DISCONNECTED, CONNECTING, CONNECTED state;
public: void connect() switch(state) default: break; case State::DISCONNECTED: case State::CONNECTED: establish(); state = State::CONNECTING; break;
void disconnect() switch(state) default: break; case State::CONNECTING: case State::CONNECTED: close(); state = State::DISCONNECTED; break;
3 . 9
CONNECTION (V2) - ENUM/SWITCHIMPLEMENTATION
void established() state = State::CONNECTED;
void ping(const Ping& event) if (state == State::CONNECTED && is_valid(event)) resetTimeout();
void timeout() connect(); ;
3 . 10
CONNECTION (V2) - BENCHMARKGCC-7 Clang-3.9
Compilation time 0.112s 0.118s
sizeof(ConnectionV2) 1b 1b
Executable size 6.2K 6.2K
Connect: ASM x86-64
main: sub rsp, 8 mov edi, OFFSET FLAT:.LC1 call puts xor eax, eax add rsp, 8 ret
https://godbolt.org/g/3Qphjb
3 . 11
CONNECTION (V2) - SUMMARY(+) Quick compilation times(+) Good performance(+) Minimal size used(-) Hard to follow(-) Hard to extend and maintain(-) Hard to test
3 . 12
CONNECTION (V3) - C++17 VARIANTIMPLEMENTATION
class ConnectionV3 struct Disconnected ; struct Connecting ; struct Connected ;
std::variant<Disconnected, Connecting, Connected> state;
public: void connect() std::visit(overload [&](Disconnected) establish(); state = Connecting; , [&](Connected) establish(); state = Connecting; , [](auto) , state);
3 . 13
CONNECTION (V3) - C++17 VARIANTIMPLEMENTATION
void disconnect() std::visit(overload [&](Connecting) close(); state = Disconnected; , [&](Connected) close(); state = Disconnected; , [](auto) , state);
void established() state = Connected;
void ping(const Ping& event) if (std::get_if<Connected>(&state) && is_valid(event)) resetTimeout();
void timeout() connect(); ;
3 . 14
CONNECTION (V3) - BENCHMARKGCC-7 Clang-3.9
Compilation time 0.272s 0.269s
sizeof(ConnectionV3) 2b 8b
Executable size 6.2K 6.2K
Connect: ASM x86-64
main: sub rsp, 56 xor eax, eax lea rsi, [rsp+16] lea rdi, [rsp+32] mov WORD PTR [rsp+16], ax mov QWORD PTR [rsp+8], rsi movq xmm0, QWORD PTR [rsp+8] ...
https://godbolt.org/g/yf1BjN
3 . 15
CONNECTION (V3) - SUMMARY(+) Data connected with the state naturally(+) Quick compilation times(-) Poor performance(-) Uses more size than required (different on GCC andClang)(-) Hard to follow(-) Hard to extend and maintain(-) Hard to test
3 . 16
LET'S GO BACK TO THE CONNECTION STATEDIAGRAM (UML 2.5)
3 . 17
CONNECTION: TRANSITION TABLEREPRESENTATION (UML 2.5)
3 . 18
CONNECTION: TRANSITION TABLE TEXTREPRESENTATION (UML 2.5)
* > Disconnected : connect / establish > Connecting
Connecting : established > Connected
Connected : ping [ is_valid ] / resetTimeout
Connected : timeout > Connecting
Connected : disconnect > Disconnected
3 . 19
LET'S INTRODUCE [BOOST].SMLCONNECTION (V4)
sml::sm connectionV4 = [] // C++17 template arg. deduction for class templates return transition_table * "Disconnected"_s + connect / establish = "Connecting"_s, "Connecting"_s + established = "Connected"_s, "Connected"_s + ping [ is_valid ] / resetTimeout, "Connected"_s + timeout / establish = "Connecting"_s, "Connected"_s + disconnect / close = "Disconnected"_s ; ;
3 . 20
CONNECTION (V4) - [BOOST].SMLACTIONS
const auto establish = [] std::puts("establish!"); ; const auto disconnect = [] std::puts("disconnect!"); ;
GUARDSconst auto is_valid = [](auto event) return true; ;
3 . 21
CONNECTION (V4) - BENCHMARKGCC-7 Clang-3.9
Compilation time 0.151s 0.169s
sizeof(ConnectionV4) 1b 1b
Executable size 6.2K 6.2K
Connect: ASM x86-64
main: subq $8, %rsp movl $.LC0, %edi call puts xorl %eax, %eax addq $8, %rsp ret
https://godbolt.org/g/6z6UF4
3 . 22
CONNECTION (V4) - SUMMARY(+) Quick compilation times(+) Good performance(+) Minimal size used(+) Easy to follow(+) Easty to extend and maintain(+) Easy to test(-) Library has to be used (C++14)
3 . 23
CONNECTION - BENCHMARKNaive Enum/Switch Variant [Boost].SML
Compilationtime
0.101s 0.112s 0.269s 0.151s
sizeof(sm) 3b 1b 2b/8b 1b
Executablesize
6.2K 6.2K 6.2K 6.2K
Performance(connect)
inlined inlined not-inlined
inlined
3 . 24
MORE REALIST BENCHMARK
Events States Transitions Process Events
50 50 50 1'000'000
3 . 25
MORE REALIST BENCHMARKMAIN
int main() for (auto i = 0; i < 1'000'000; ++i) if (rand() % 2) sm.process_event(e1()); if (rand() % 2) sm.process_event(e2()); if (rand() % 2) sm.process_event(e3()); ... if (rand() % 2) sm.process_event(e100());
CXXFLAGS$CXX std=c++1z O3 flto fnoexceptions DNDEBUG benchmark.cpp
3 . 26
MORE REALIST - RESULTSBENCHMARKEnum/Switch Variant [Boost].SML
Compilationtime
0.132s 15.321s 0.582s
Execution time 679ms 827ms 622ms
Memory usage 1b 2b/8b 1b
Executable size 15K 187K 34K
Line of Code(LOC)
~300 (nomacros)
~300 ~50
Median / 100 runs
4 . 1
[BOOST].SMLUML-2.5 State Machine Language
https://github.com/boost-experimental/sml
4 . 2
[BOOST].SMLOne header - 2k LOC - (boost/sml.hpp) / generatedNeither Boost nor STL is requiredQuick compilation-times (-Wall -Wextra -Werror -pedantic-pedantic-errors)Blazing fast run-time (Generated at compile-time)No 'virtual's (-fno-rtti)Optional support for 'exception's (-fno-exceptions)Supported compilers (C++14)
, , , Clang-3.4+ XCode-6.1+ GCC-5.2+ MSVC-2015+
4 . 3
- API (SIMPLIFIED)[BOOST].SML/** * Makes transition table from DSL * @tparam Ts... transitions (transitional) */ template <class... Ts> requires transitional<Ts>()... struct transition_table;
/** * Helper function to make transition_table (C++14) * @tparam Ts... transitions (transitional) * @return transition_table */ template <class... Ts> requires transitional<Ts>()... constexpr auto make_transition_table(Ts&&...) noexcept;
4 . 4
- API (SIMPLIFIED)[BOOST].SML/** * State Machine * * @tparam T Callable object returning transition_table * @tparam TPolicies policies to be applied such as * thread safe, exception safe, etc. */ template<class T, class... TPolicies> requires callable<transition_table, T>() class sm;
/** * Process event * Complexity O(1) * @param TEvent event to be processed */ template<class TEvent> constexpr void process_event(const TEvent&);
4 . 5
- FEATURES[BOOST].SMLUML (2.5)
TransitionAnonymous, Internal, Self, Local transitionGuard, Action
Unexpected, Deffered, Any eventState
Entry/Exit Actions, Initial, Terminate stateComposite/Sub state
Explicit entry/exit, ForkShallow History, Deep History
Orthogonal regionsNon-UML
Logging, State visitor, Diagram gen., Run-time dispatch
4 . 6
- FEATURES[BOOST].SMLWe DON'T pay for features we are NOT using!
For example, if the state machine doesn't useorthogonal regions, the code responsible for
handling them won't be even generated
- MORE REALISTIC EXAMPLE (SYSTEM)[BOOST].SML
4 . 7
Enum/Switch? No, thank you!
4 . 8
- MORE REALISTIC EXAMPLE (SYSTEM)[BOOST].SMLstruct Connection auto operator()() const
return transition_table "Disconnected"_s(H) + connect / []establish(); = "Connecting"_s, "Connecting"_s + established = "Connected"_s, "Connected"_s + ping [ is_valid ] / []resetTimeout(); "Connected"_s + timeout / []establish(); = "Connecting"_s, "Connected"_s + disconnect / []close(); = "Disconnected"_s ; ;
struct System auto operator()() const
return transition_table * "Idle"_s + power_up [ has_battery and is_healthy] / connect = state<Connection>, state<Connection> + suspend = "Suspended"_s, "Suspended"_s + resume = state<Connection>, // // * "Watchdog"_s + tick / resetTimeout "Watchdog"_s + timeout = X ;
4 . 9
- MORE REALISTIC EXAMPLE (SYSTEM)[BOOST].SMLint main() using namespace sml; sm<System> system;
system.process_event(power_up); assert(system.is(state<Connection>, "Watchdog"_s));
system.process_event(suspend); assert(system.is("Suspended"_s, "Watchdog"_s));
system.process_event(timeout); assert(system.is(X)); // true if any state is in terminated state (X)
4 . 10
[BOOST].SML VS BOOST.MSM-EUML VSBOOST.STATECHART
4 . 11
OVERVIEWLibrary [Boost].SML Boost.MSM-
eUMLBoost.Statechart
Standard C++14 C++98/03 C++98/03
Version 1.0.1 1.63 1.63
License Boost 1.0 Boost 1.0 Boost 1.0
Linkage header only header only header only
4 . 12
(SAME AS BEFORE)BENCHMARK
Events States Transitions Process Events
50 50 50 1'000'000
4 . 13
- RESULTSBENCHMARK[Boost].SML Boost.MSM-
eUMLBoost.Statechart
Compilationtime
0.582s 1m15.935s 5.671s
Executiontime
622ms 664ms 2282ms
Memoryusage
1b 120b 224b
Executablesize
34K 611K 211K
5 . 2
TRANSITIONAL CONCEPTtemplate <class T> concept bool transitional() return requires(T transition) typename T::src_state; typename T::dst_state; typename T::event; T::property; // initial, history state, etc.
transition.execute(const typename T::Event&) > optional<state_t>; ;
5 . 3
FRONT ENDDOMAIN SPECIFIC LANGUAGE (DSL)
static_assert(std::is_same< decltype(
transition_table * "Disconnected"_s + connect / establish = "Connecting"_s, "Connecting"_s + established = "Connected"_s ),
transition_table< // // // src_state dst_state event guard action // // // transition<state<"Disconnected">, state<"Connecting">, connect, always, establish>, transition<state<"Connecting">, state<"Connected">, established, always, none> > >);
5 . 4
BACK-ENDGENERATED AT COMPILE TIME MAPPING PER EVENT AND STATE
using mappings_t = type_list< pair<connect, type_list< transitions< transition<state<"Disconnected">, state<"Connecting">, connect, always, establish> >, transitions<>, // Connecting (unexpected event) transitions<> // Connected (unexpected event) >, pair<established, type_list< transitions<>, // Disconnected (unexpected event) transitions< transition<state<"Connecting">, state<"Connected">, established, always, none> >, transitions<> // Connected (unexpected event) >
5 . 5
BACK-ENDPROCESS EVENT - JUMP TABLE - (SIMPLIFIED)
template<class TEvent> constexpr void process_event(const TEvent& event) process_event_impl(event, get_mappings_t<TEvent>);
template<class... Transitions, class TEvent> constexpr void process_event_impl(const TEvent& event, type_list<Transitions...>) const static (*dispatch_table[])(const TEvent&) = &Transitions::template execute<TEvent>... ; dispatch_table[current_state](event); // Complexity: O(1) jump table
6 . 1
SUMMARY
6 . 2
IF YOU LIKE IT OR NOT, YOUR CODE WON'T BESTATEFUL (MOST LIKELY)
6 . 3
IMPLICIT/HAND WRITTEN STATE MACHINESARE HARD TOReason aboutMaintain / ExtendTest
6 . 5
LEVERAGING ZERO-COST LIBRARIES WILL BOOSTYOUR DESIGN AND/OR PERFRORMANCE
/ [Boost].SML Boost.MSM
7
QUESTIONS?[Boost].SML
Documentation
Source Code
Try it online!
http://boost-experimental.github.io/sml
https://github.com/boost-experimental/sml
http://boost-experimental.github.io/sml/examples
https://godbolt.org/g/UX6eWt
-
| | [email protected] @krisjusiak linkedin.com/in/kris-jusiak