Repository navigation
Reduce FSM<TState, TData> allocations - #6145
Conversation
* Made `Event` a `readonly struct` * Eliminated unnecessary `List<object>` allocations * Cleaned up XML-DOC comments
Aaronontheweb
left a comment
There was a problem hiding this comment.
Described API and memory allocation changes.
| public override string ToString() { } | ||
| } | ||
| public sealed class Event<TD> : Akka.Actor.INoSerializationVerificationNeeded | ||
| public struct Event<TD> : Akka.Actor.INoSerializationVerificationNeeded |
There was a problem hiding this comment.
Technically a binary breaking change maybe, but it's source compatible.
| public static Akka.Actor.FSMBase.StateTimeout Instance { get; } | ||
| } | ||
| public class State<TS, TD> : System.IEquatable<Akka.Actor.FSMBase.State<TS, TD>> | ||
| public sealed class State<TS, TD> : System.IEquatable<Akka.Actor.FSMBase.State<TS, TD>> |
There was a problem hiding this comment.
sealed the State class for better perf.
| public State(TS stateName, TD stateData, TimeSpan? timeout = null, Reason stopReason = null, IReadOnlyList<object> replies = null, bool notifies = true) | ||
| { | ||
| Replies = replies ?? new List<object>(); | ||
| Replies = replies ?? Array.Empty<object>(); |
There was a problem hiding this comment.
Eliminated unnecessary List<object> allocation.
| internal State<TS, TD> WithNotification(bool notifies) | ||
| { | ||
| // don't bother allocating even a stack type if the notifies value is identical. | ||
| if (Notifies == notifies) |
There was a problem hiding this comment.
Don't return a new copy of this object if the current Notifies value matches the new one (it usually does.)
| /// </summary> | ||
| /// <typeparam name="TD">The state data for this event</typeparam> | ||
| public sealed class Event<TD> : INoSerializationVerificationNeeded | ||
| public readonly struct Event<TD> : INoSerializationVerificationNeeded |
There was a problem hiding this comment.
We allocate one of these for every message we process, so changing to a readonly struct here greatly reduces Gen 0 / 1 GC.
| public State<TState, TData> Stay() | ||
| { | ||
| return GoTo(_currentState.StateName).WithNotification(false); | ||
| return _currentState.WithNotification(false); |
There was a problem hiding this comment.
Biggest memory savings - don't allocate a new State object each time we call Stay. Re-use the same value.
There was a problem hiding this comment.
FWIW, there's no benefit to copying the same state over and over again here. The Reason, Replies properties et al don't have any effects when used here in combination with WithNotification(false).
|
Looked at making |
|
Looks like this change definitely caused an issue with the |
Spec could also be racy - worked on my machine. |
Nope, definitely an error related to my changes. Will investigate. |
|
Benchmarks based on latest changes. BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.2006 (21H2)
AMD Ryzen 7 1700, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.201
[Host] : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT
DefaultJob : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT
|
* close akkadotnet#2560 - added performance benchmarks for FSM * Improved FSM memory consumption * Made `Event` a `readonly struct` * Eliminated unnecessary `List<object>` allocations * Cleaned up XML-DOC comments * don't return new `State<TState, TData>` during `Stay()` * API approvals * fixed `State<TS.,TD>` errors
Changes
Reduced some unnecessary allocations inside the
FSM<TState, TData>class.Checklist
For significant changes, please ensure that the following have been completed (delete if not relevant):
Latest
v1.4BenchmarksThis PR's Benchmarks
48% memory reduction, 14% throughput improvement