Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost Statechart vs. Meta State Machine

People also ask

Should I use state machine?

State machines are useful if you can define a clear number of states and events that will trigger transitions to them. They might be good for user interfaces or communications protocols. However, when building complex systems, implementation of state machines is not the best option.

What are state machines good for?

State Machines are used in applications where distinguishable states exist. Each state can lead to one or multiple states and can also end the process flow. A State Machine relies on user input or in-state calculation to determine which state to go to next.

How do you explain a state machine?

A state machine is a behavior model. It consists of a finite number of states and is therefore also called finite-state machine (FSM). Based on the current state and a given input the machine performs state transitions and produces outputs.


As there seems to be much interest, please allow me to give my (obviously biased) opinion, which should therefore be taken with a grain of salt:

  • MSM is much faster
  • MSM requires no RTTI or anything virtual
  • MSM has a more complete UML2 support (for example internal transitions, UML-conform orthogonal regions)
  • MSM offers a descriptive language (actually several). For example, using the eUML front-end, a transition can be described as Source + Event [Guard] / Action == Target
  • MSM will make your compiler suffer for bigger state machines, so you will need a pretty recent compiler (g++ >= 4.x, VC >= 9)

You can make yourself a better opinion by looking for comments posted during the review of MSM. This subject was much discussed on the developer list.


As Christophe has already mentioned, one of the key differences between the two libraries is runtime performance. While MSM probably offers the best you can get here, Statechart consciously trades memory and processor cycles towards better scalability.

With Boost.Statechart you can spread the layout (i.e. states, transitions) of your state machine over multiple translation units (cpp files) in ways you can't with MSM. This allows you to make the implementation of large FSMs more maintainable and get much faster compilation than with MSM.

Whether or not the performance overhead of Statechart compared to MSM will actually be significant for your application is often quite easy to answer when you ask yourself how many events your app will have to process per second.

Assuming a moderately complex FSM implemented with Boost.Statechart, here are a few ballpark numbers:

  • Most current PC hardware will easily cope with >100'000 events per second
  • Even very resource-constrained hardware will be able to process a few hundred events per second.

Regarding CPU load, if the number of events to process is much lower than these numbers, Boost.Statechart overhead compared to MSM will almost certainly not be noticeable. If the number is much higher, you're definitely better off with MSM.

More in-depth information on the performance/scalability tradeoffs can be found here: http://www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html


While coding my own PPP implementation I used Statechart for three reasons: 1) Statechart is simpler and has clearer documentation; 2) I really dislike UML :)

Boost docs say MSM is at least 20 times faster, but compiles pretty slow for large FSM.


Some time ago I began with Statechart and moved to MSM because it was easier to use in conjunction with asio from a single thread. I did not manage to mesh Statechart and its multithreading capabilities with my use of asio - it was likely some sort of newbie incomprehension of Statechart on my part. I found that MSM was easier to use as it did not address multithreading.


In answer to Tim's late entry to the discussion (which also addresses one of the very early comments from Lev).

As one of those who argued for exit separation from destructors in statechart (argument based on a real use case, about interaction with the real world i.e. I/O) way back when it was submitted to Boost I agree there can be issues in putting exit logic in destructors. David Abrahams unsurprisingly made persuasive arguments regarding exception safety as well. For those reasons Statechart doesn't require you to put logic in destructors - but it allows you to - with the usual advice.

Logic that should only ever run as part of a transition out of a state (not destruction of the statechart object as a whole) can (and should if there is also resource cleanup to do) be separated into a separate exit() action.

For a "thin" state with no active state (resources), just entry/exit actions to perform, you can perform those actions in ctor and d'tor and make sure the constructor and destructor don't throw. There is no reason for them to - there is no state to perform RAII on - there is no evil in having the error handling in these places raise appropriate events. You may still need to consider whether you want exit actions that alter external state to run on state machine destruction though... and put them in exit action if you don't want them to occur in this case...

Statechart models activation as instantiation of an object, so if your constructor has real work/activation/instantiation to do and if it is able to fail such that the state cannot be entered Statechart supports that by giving you the ability to map an exception to an event. This is handled in a way that works up the state hierarchy looking for an outer state that handles the exception event, analogous to the way the stack would have unwound for a call stack based invocation model.

This is all well documented - I suggest you read the docs and try it. I suggest that you use destructors to clean up "software resources" and exit actions to perform "real-world exit actions".

It is worth noting note that exception propagation is a bit of a problem in all event driven environments, not just statecharts. It is best to reason about and include faults/errors in your statechart design and if and only if you can't handle them another way resort to exception mapping. At least that works for me - ymmmv....