文章目录
引言
TinyFSM是一个为C++设计的轻量级有限状态机开源库库。
在嵌入式系统开发中,TinyFSM等状态机适用于控制系统和通信协议等场景,然而,开发者也需考虑该库的性能并考虑是否遵循工业C++标准。
传统 C++ 实现不仅能很容易的满足工业标准的要求,还能提供更高的性能和更低的内存开销。
现代 C++ 实现虽然引入了许多新特性,可以简化代码结构,但在性能上可能不如传统 C++ 实现高效。
反而TinyFSM本身很多地方设计不满足工业C++标准。
基于传统 C++ 实现的状态机
在嵌入式系统中,传统 C++ 实现的状态机通过显式管理状态变量和使用 switch
语句处理事件,可以有效控制内存和运行时开销,同时确保代码符合 MISRA C++ 规范。以下是一个简单的门状态机示例:
cpp
#include <iostream>
enum class DoorState { Closed, Open, Locked };
class DoorStateMachine {
public:
DoorStateMachine() : state(DoorState::Closed) {}
void open() {
switch (state) {
case DoorState::Closed:
std::cout << "Door is opened\n";
state = DoorState::Open;
break;
case DoorState::Open:
std::cout << "Door is already open\n";
break;
case DoorState::Locked:
std::cout << "Cannot open, door is locked\n";
break;
}
}
void close() {
switch (state) {
case DoorState::Closed:
std::cout << "Door is already closed\n";
break;
case DoorState::Open:
std::cout << "Door is closed\n";
state = DoorState::Closed;
break;
case DoorState::Locked:
std::cout << "Cannot close, door is locked\n";
break;
}
}
void lock() {
switch (state) {
case DoorState::Closed:
std::cout << "Door is locked\n";
state = DoorState::Locked;
break;
case DoorState::Open:
std::cout << "Cannot lock, door is open\n";
break;
case DoorState::Locked:
std::cout << "Door is already locked\n";
break;
}
}
private:
DoorState state;
};
int main() {
DoorStateMachine door;
door.open();
door.close();
door.lock();
door.open();
return 0;
}
TinyFSM 实现的对比
TinyFSM 是一个轻量级状态机库,通过继承 tinyfsm::Fsm
和定义状态类,能够直观地定义状态和事件处理函数。以下是使用 TinyFSM 实现的门状态机代码:
cpp
#include <tinyfsm.hpp>
#include <iostream>
struct OpenEvent : tinyfsm::Event {};
struct CloseEvent : tinyfsm::Event {};
struct LockEvent : tinyfsm::Event {};
class DoorState : public tinyfsm::Fsm<DoorState> {
public:
virtual void react(OpenEvent const &) { std::cout << "Invalid transition\n"; }
virtual void react(CloseEvent const &) { std::cout << "Invalid transition\n"; }
virtual void react(LockEvent const &) { std::cout << "Invalid transition\n"; }
virtual void entry() {}
virtual void exit() {}
};
class Closed : public DoorState {
public:
void react(OpenEvent const &) override {
std::cout << "Door is opened\n";
transit<Open>();
}
void react(LockEvent const &) override {
std::cout << "Door is locked\n";
transit<Locked>();
}
};
class Open : public DoorState {
public:
void react(CloseEvent const &) override {
std::cout << "Door is closed\n";
transit<Closed>();
}
};
class Locked : public DoorState {
public:
void react(OpenEvent const &) override {
std::cout << "Cannot open, door is locked\n";
}
};
FSM_INITIAL_STATE(DoorState, Closed)
int main() {
DoorState::start();
DoorState::dispatch(OpenEvent());
DoorState::dispatch(CloseEvent());
DoorState::dispatch(LockEvent());
DoorState::dispatch(OpenEvent());
return 0;
}
现代 C++ 实现的状态机
现代 C++(如 C++14 和 C++17)引入了许多新特性,使得开发高效、可维护的代码更加容易。在状态机实现中,现代 C++ 特性如 std::function
和 std::unordered_map
可以显著简化代码结构。以下是一个基于现代 C++ 实现的状态机示例:
cpp
#include <iostream>
#include <functional>
#include <unordered_map>
enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };
class DoorStateMachine {
public:
DoorStateMachine() : state(DoorState::Closed) {
stateHandlers[DoorState::Closed][DoorEvent::OpenEvent] = [this]() { handleOpenFromClosed(); };
stateHandlers[DoorState::Closed][DoorEvent::LockEvent] = [this]() { handleLockFromClosed(); };
stateHandlers[DoorState::Open][DoorEvent::CloseEvent] = [this]() { handleCloseFromOpen(); };
stateHandlers[DoorState::Locked][DoorEvent::OpenEvent] = [this]() { handleOpenFromLocked(); };
}
void handleEvent(DoorEvent event) {
auto eventHandler = stateHandlers[state].find(event);
if (eventHandler != stateHandlers[state].end()) {
eventHandler->second();
} else {
std::cout << "Invalid event\n";
}
}
private:
void handleOpenFromClosed() {
std::cout << "Door is opened\n";
state = DoorState::Open;
}
void handleLockFromClosed() {
std::cout << "Door is locked\n";
state = DoorState::Locked;
}
void handleCloseFromOpen() {
std::cout << "Door is closed\n";
state = DoorState::Closed;
}
void handleOpenFromLocked() {
std::cout << "Cannot open, door is locked\n";
}
DoorState state;
std::unordered_map<DoorState, std::unordered_map<DoorEvent, std::function<void()>>> stateHandlers;
};
int main() {
DoorStateMachine door;
door.handleEvent(DoorEvent::OpenEvent);
door.handleEvent(DoorEvent::CloseEvent);
door.handleEvent(DoorEvent::LockEvent);
door.handleEvent(DoorEvent::OpenEvent);
return 0;
}
性能对比
-
以下在树莓派5上测试,基本信息如下
CPU:2.4GHz 四核 64位 Arm Cortex-A76 内存:32位 LPDDR4X SDRAM,4267MT/s
-
使用了benchmark多次深度压测。
-
平均数据分别为:
TinyFSM 传统C++ 现代C++
5.91 ns 1.25 ns 413 ns
TinyFSM 性能测试
cpp
#include <benchmark/benchmark.h>
#include <tinyfsm.hpp>
// 事件定义
struct OpenEvent : tinyfsm::Event {};
struct CloseEvent : tinyfsm::Event {};
struct LockEvent : tinyfsm::Event {};
// 状态机基类
class DoorState : public tinyfsm::Fsm<DoorState> {
public:
virtual void react(OpenEvent const &) {}
virtual void react(CloseEvent const &) {}
virtual void react(LockEvent const &) {}
virtual void entry() {}
virtual void exit() {}
};
// 定义具体状态类
class Closed : public DoorState {
public:
void react(OpenEvent const &) override { transit<Open>(); }
void react(LockEvent const &) override { transit<Locked>(); }
};
class Open : public DoorState {
public:
void react(CloseEvent const &) override { transit<Closed>(); }
};
class Locked : public DoorState {
public:
void react(OpenEvent const &) override {}
};
FSM_INITIAL_STATE(DoorState, Closed)
static void BM_TinyFSM(benchmark::State &state) {
for (auto _ : state) {
DoorState::start();
DoorState::dispatch(OpenEvent());
DoorState::dispatch(CloseEvent());
DoorState::dispatch(LockEvent());
}
}
BENCHMARK(BM_TinyFSM);
BENCHMARK_MAIN();
测试结果
:
Benchmark Time CPU Iterations
-----------------------------------------------------
BM_TinyFSM 5.91 ns 5.91 ns 116692670
传统 C++ 性能测试
cpp
#include <benchmark/benchmark.h>
enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };
class DoorStateMachine {
public:
DoorStateMachine() : state(DoorState::Closed) {}
void handleEvent(DoorEvent event) {
switch (state) {
case DoorState::Closed:
if (event == DoorEvent::OpenEvent) {
state = DoorState::Open;
} else if (event == DoorEvent::LockEvent) {
state = DoorState::Locked;
}
break;
case DoorState::Open:
if (event == DoorEvent::CloseEvent) {
state = DoorState::Closed;
}
break;
case DoorState::Locked:
break;
}
}
private:
DoorState state;
};
static void BM_TraditionalCPPStateMachine(benchmark::State& state) {
DoorStateMachine door;
for (auto _ : state) {
benchmark::DoNotOptimize(door);
door.handleEvent(DoorEvent::OpenEvent);
door.handleEvent(DoorEvent::CloseEvent);
door.handleEvent(DoorEvent::LockEvent);
}
}
BENCHMARK(BM_TraditionalCPPStateMachine);
BENCHMARK_MAIN();
测试结果:
Benchmark Time CPU Iterations
-------------------------------------------------------------------
BM_TraditionalCPPStateMachine 1.25 ns 1.25 ns 558856294
现代 C++ 性能测试
cpp
#include <benchmark/benchmark.h>
#include <functional>
#include <unordered_map>
enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };
class DoorStateMachine {
public:
DoorStateMachine() : state(DoorState::Closed) {
stateHandlers[DoorState::Closed][DoorEvent::OpenEvent] = [this]() { state = DoorState::Open; };
stateHandlers[DoorState::Closed][DoorEvent::LockEvent] = [this]() { state = DoorState::Locked; };
stateHandlers[DoorState::Open][DoorEvent::CloseEvent] = [this]() { state = DoorState::Closed; };
}
void handleEvent(DoorEvent event) {
auto eventHandler = stateHandlers[state].find(event);
if (eventHandler != stateHandlers[state].end()) {
eventHandler->second();
}
}
private:
DoorState state;
std::unordered_map<DoorState, std::unordered_map<DoorEvent, std::function<void()>>> stateHandlers;
};
static void BM_ModernCPPStateMachine(benchmark::State& state) {
for (auto _ : state) {
DoorStateMachine door;
door.handleEvent(DoorEvent::OpenEvent);
door.handleEvent(DoorEvent::CloseEvent);
door.handleEvent(DoorEvent::LockEvent);
}
}
BENCHMARK(BM_ModernCPPStateMachine);
BENCHMARK_MAIN();
测试结果:
Benchmark Time CPU Iterations
-------------------------------------------------------------------
BM_ModernCPPStateMachine 413 ns 413 ns 1694224
工业Misra C++编程标准
MISRA C++ 是工业领域的一个要求比较高的标准。以下是一些多态和继承的规则:
- 规则 10-3-1:虚函数应有明确的用途,避免不必要的虚函数调用。
- 规则 10-3-2:禁止多重继承。
- 规则 10-3-3:尽量避免继承深度超过两个层次。
- 规则 10-3-4:构造函数和析构函数中不应调用虚函数。
- 规则 10-3-5:禁止多态对象的拷贝和赋值。
TinyFSM 的优缺点分析
优点:
- 简洁和易用性:通过继承和定义状态类,TinyFSM 使状态和事件处理函数的定义更加直观。
- 代码可读性:每个状态独立成类,使状态转换逻辑清晰明了,便于理解和维护。
- 减少错误:提供了一个经过验证的框架,降低了手动管理状态转换时的出错风险。
- 可扩展性:可以轻松添加新状态和事件,只需定义新的状态类和事件类型。
缺点:
- 内存使用:使用多态和虚函数增加了对象的内存开销,对于内存资源有限的嵌入式系统可能不太合适。
- 运行时开销:虚函数调用需要通过虚表查找实际的函数地址,增加了运行时开销。
- 不符合工业编码规范:TinyFSM 不符合严格的工业编码规范(如 MISRA C++)。
结论
总之,工业领域是否要选择TinyFSM还需要三思,尽管现代编程技术如 TinyFSM 对代码结构的简化带来了吸引力,但在需要遵循严格工业标准的环境中,推荐采用更传统的 C++ 编程方法。