Open ghost opened 7 years ago
Hi @notsonotso,
One possible workaround if you suspect TypeState as a memory culprit is to build a factory that constructs FSMs on demand given a set of shared states and transition functions.
If you are building FSMs from a state enum, those pieces should be relatively small (fromStates
and toStates
). The biggest memory hassle would be duplicate callback handlers and transition functions being defined. (Although depending on the complexity of your FSM this might not be a problem)
My example shows how to reuse handlers, and hack shared transition functions into such a factory. Profiling this with a 500 FSMs from the factory kept the heap to ~1kb (which is mostly just FSM instances and not duplicated handlers or transition functions).
Let me know if this isn't what you're looking for, or if I'm off track.
// typescript 2.4.2
enum States {
A = "A",
B = "B",
C = "C",
D = "D"
}
let ClientFSMFactory = function(){
let onACallback = (from) => { console.log(from) };
let onBCallback = (from) => { console.log(from) };
let onCCallback = (from) => { console.log(from) };
let onDCallback = (from) => { console.log(from) };
let fromAtoB = new typestate.TransitionFunction<States>(null, States.A, States.B);
let fromBtoC = new typestate.TransitionFunction<States>(null, States.B, States.C);
let fromCtoD = new typestate.TransitionFunction<States>(null, States.C, States.D);
let fromDtoA = new typestate.TransitionFunction<States>(null, States.D, States.A);
let tnsFcns = [fromAtoB, fromBtoC, fromCtoD, fromDtoA];
return {
getFSM: () => {
let fsm = new typestate.FiniteStateMachine<States>(States.A);
(<any>fsm)._transitionFunctions = tnsFcns;
fsm.on(States.A, onACallback);
fsm.on(States.B, onBCallback);
fsm.on(States.C, onCCallback);
fsm.on(States.D, onDCallback);
return fsm;
}
}
}
let factory = ClientFSMFactory();
let fsms: typestate.FiniteStateMachine<States>[] = [];
for (let i = 0; i < 500; i++) {
fsms.push(factory.getFSM());
}
let currentState = 1;
let possibleStates = [States.A, States.B, States.C, States.D];
function runfsms(){
for (let state of possibleStates){
for (let fsm of fsms) {
fsm.go(state)
console.log("Current state:", fsm.currentState);
console.log("Current state:", fsm.currentState);
}
}
}
runfsms();
I'm a little hesitant to create an duplicate FSM feature that shares the internal state, shared state is a recipe for subtle bugs.
However, it may be worthwhile to allow transition functions to easily exist independent of a FSM since they only really describe a valid transition and contain no state so could easily be shared. This would allow for an easier experience building the factory I described.
I've opened an issue to allow this behavior https://github.com/eonarheim/TypeState/issues/21
Thanks for looking into this! I'll try your workaround.
Profiling this with a 500 FSMs from the factory kept the heap to ~1kb
To be honest, I wish this was a bit lower. This overhead will be added to all (~20.000) connected clients and is definitely enough to introduce some serious GC pressure. Not sure what can be done, though.
I'm a little hesitant to create an duplicate FSM feature that shares the internal state, shared state is a recipe for subtle bugs.
Fair point. That said, I would be fine with the library throwing an error if attempting to alter transitions once sharing has occurred.
If you are facing GC pause pressure from object overhead right now, we could reorganized that factory into a pool and pre-allocate ~20,000 FSM at process start. Then you'd only see memory increases/GCs when you need to resize the pool. This is a good example of what I'm talking about https://www.html5rocks.com/en/tutorials/speed/static-mem-pools/
Reseting a FSM after a client connection should be pretty simple (I think you'll only need to zero out the transition functions and reset .currentState
to whatever your start state should be).
What kind of constraints are you working with right now on your server?
I have a situation where I have a lot of FiniteStateMachine instances (one per connected client), but they all have the same transitions defined.
To reduce memory consumption, it would be great if these could share the defined transitions.
Would it be possible to implement an API that creates an FSM based on an existing one?
I belive this is the information that needs to be shared:
Any good workarounds in the current implementation?