【问题标题】:Efficient state machine pattern in javajava中的高效状态机模式
【发布时间】:2012-10-22 01:21:18
【问题描述】:

我正在编写一个 java 模拟应用程序,它有很多实体要模拟。这些实体中的每一个在系统中的任何时候都具有一定的状态。对此类实体建模的一种可能且自然的方法是使用state (or state machine) 模式。问题是如果有很多状态切换,它会在运行时创建很多对象,这可能会导致系统性能不佳。我有哪些设计替代方案?我希望性能成为可维护性之后的主要标准。

谢谢

【问题讨论】:

  • 您对对象创建将成为模拟瓶颈的把握有多大?
  • 我在考虑 enum + switch
  • "might" 是您问题中的关键字:您创建了更多您想要的对象这一事实并不意味着性能无法接受!在分析之前进行优化是个坏主意;编写理想的解决方案,然后进行优化。
  • 实体集可能会变得非常大。如果许多实体在一个模拟步骤中更改其状态,则可能会产生巨大的对象创建开销,因为每次实体执行状态转换时,状态机模式都会创建一个新的状态对象。
  • 我也认为存储在数组中的枚举加上 switch 语句可能是您的最佳解决方案。

标签: java performance simulation state-machine


【解决方案1】:

以下代码将为您提供高性能(~10ns/事件)零运行时 GC 状态机实现。只要您在系统或组件中有状态的概念,就使用显式状态机,这不仅使代码干净和可扩展,而且让人们(甚至不是程序员)可以立即看到系统所做的事情,而无需挖掘大量回调:

abstract class Machine {
    enum State {
      ERROR,
      INITIAL,
      STATE_0,
      STATE_1,
      STATE_2;
    }

    enum Event {
      EVENT_0,
      EVENT_1,
      EVENT_2;
    }

    public static final int[][] fsm;
    static {
      fsm = new int[State.values().length][];
      for (State s: State.values()) {
        fsm[s.ordinal()] = new int[Event.values().length];
      }
    }

    protected State state = State.INITIAL;
    // child class constructor example
    // public Machine() {
    //   // specify allowed transitions
    //   fsm[State.INITIAL.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    // }

    public final void onEvent(Event event) {
      final State next = State.values()[ fsm[state.ordinal()][event.ordinal()] ];
      if (next ==  State.ERROR) throw new RuntimeException("invalid state transition");
      if (acceptEvent(event)) {
        final State prev = state;
        state = next;
        handleEvent(prev, event);
      }
    }

    public abstract boolean acceptEvent(Event event);
    public abstract void handleEvent(State prev, Event event);
}

如果将 fsm 替换为大小为 S*E 的单向数组,它还将改善状态机的缓存邻近特性。

【讨论】:

  • 本机数组分配有新的(零 GC?),要查找,您必须编写自己的“for”循环。表现?事件和状态不能是通用的,它们是特定于每个具体类的。
  • @Aubin - 运行时 GC 为零。我已经演示了保证高性能和 SxN 规模的技术,可以尽可能泛化类,可以将转换表带到子类等。
【解决方案2】:

我的建议:
您是否可以配置“转换管理”(即通过 XML)。

将 XML 加载到保存状态的存储库。
内部数据结构将是一个 Map:

Map<String,Map<String,Pair<String,StateChangeHandler>>> transitions;

我选择的原因是这将是一张来自州名的地图
到“输入”和新状态的地图:
每个映射定义了可能的输入和它导致的新状态之间的映射,该映射由状态名称和 StateChangeHandler 定义,稍后我将详细说明
存储库中的更改状态方法将具有以下签名:

void changeState(StateOwner owner, String input)

这样,对于使用它的状态所有者而言,存储库是无状态的,您可以复制一份,
而不必担心线程安全问题。
StateOwner 将是您的需要状态更改的类应该实现的接口。
我认为界面应该是这样的:

public interace StateOwner {
   String getState();
   void String setState(String newState);
}

此外,您将拥有一个 ChangeStateHandler 接口:

public interface StateChangeHandler {
    void onChangeState(StateOwner, String newState) {
    }
}

当仓库的changeState方法被调用时,它会
检查 stateOwner 的当前状态是否具有“输入”映射的数据结构。 如果它有这样的映射,它将检查输入是否有要更改的新状态,并调用 onChangeState 方法。
我建议您使用 StateChangeHandler 的默认实现,当然还有更明确​​地定义状态更改行为的子类。

正如我之前提到的,所有这些都可以从 XML 配置中加载,并且使用反射,您可以根据名称(如 XML 中所述)实例化 StateChangeHandler 对象,并将其保存在存储库中。


效率和良好的性能取决于并使用以下几点获得:
一种。存储库本身是无状态的 - 不应保留 StateOwner 的内部引用。
湾。您在系统启动时加载一次 XML,之后您应该使用内存数据结构。
C。您将仅在需要时提供特定的 StateChangeHandler 实现,默认实现基本上什么都不做。
d。无需实例化 Handlers 的新对象(因为它们应该是无状态的)

【讨论】:

    【解决方案3】:

    这个提议不是通用的,不是UML compliant,而是simple thing, it's a simple mean

    import java.util.HashMap;
    import java.util.Map;
    
    class Mobile1
    {
       enum State {
          FIRST, SECOND, THIRD
       }
    
       enum Event {
          FIRST, SECOND, THIRD
       }
    
       public Mobile1() {       // initialization may be done by loading a file
          Map< Event, State > tr;
          tr = new HashMap<>();
          tr.put( Event.FIRST, State.SECOND );
          _fsm.put( State.FIRST, tr );
          tr = new HashMap<>();
          tr.put( Event.SECOND, State.THIRD );
          _fsm.put( State.SECOND, tr );
          tr = new HashMap<>();
          tr.put( Event.THIRD, State.FIRST );
          _fsm.put( State.THIRD, tr );
       }
    
       public void activity() {        // May be a long process, generating events,
          System.err.println( _state );// to opposite to "action()" see below
       }
    
       public void handleEvent( Event event ) {
          Map< Event, State > trs = _fsm.get( _state );
          if( trs != null ) {
             State futur = trs.get( event );
             if( futur != null ) {
                _state = futur;
               // here we may call "action()" a small piece of code executed
               // once per transition
             }
          }
       }
    
       private final Map<
          State, Map<
             Event, State >> _fsm   = new HashMap<>();
       private /* */ State   _state = State.FIRST;
    }
    
    public class FSM_Test {
       public static void main( String[] args ) {
          Mobile1 m1 = new Mobile1();
          m1.activity();
          m1.handleEvent( Mobile1.Event.FIRST );
          m1.activity();
          m1.handleEvent( Mobile1.Event.SECOND );
          m1.activity();
          m1.handleEvent( Mobile1.Event.FIRST );   // Event not handled
          m1.activity();
          m1.handleEvent( Mobile1.Event.THIRD );
          m1.activity();
       }
    }
    

    输出:

    FIRST
    SECOND
    THIRD
    THIRD
    FIRST
    

    【讨论】:

    • 这当然很简单,但对于我使用状态机的情况来说它太过分了。有可能做得更好。
    • 我在等你的提议
    • 见上文。由 XML 驱动的类。代码太多,无法发布。 “符合 UML”是什么意思? UML 跟什么关系不大,更别提这个问题了。
    • UML 在这里或其他任何地方都不是很有用。
    猜你喜欢
    • 1970-01-01
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    相关资源
    最近更新 更多