From 9a50416dd72e38844729170a36415675f766a3fb Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 14:53:56 -0500 Subject: [PATCH 01/16] improveme child lookup performance --- src/benchmark/Akka.Benchmarks/Actor/GetChildBenchmark.cs | 4 ++-- src/core/Akka/Actor/ActorCell.Children.cs | 8 ++++---- src/core/Akka/Actor/ActorCell.cs | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/benchmark/Akka.Benchmarks/Actor/GetChildBenchmark.cs b/src/benchmark/Akka.Benchmarks/Actor/GetChildBenchmark.cs index 2e2227adfe0..2b035ede931 100644 --- a/src/benchmark/Akka.Benchmarks/Actor/GetChildBenchmark.cs +++ b/src/benchmark/Akka.Benchmarks/Actor/GetChildBenchmark.cs @@ -96,7 +96,7 @@ protected override void OnReceive(object message) private ActorWithChild.Get _getMessage = new ActorWithChild.Get("food"); private ActorWithChild.Create _createMessage = new ActorWithChild.Create("food"); - private ActorCell _cell; + private IActorContext _cell; private RepointableActorRef _repointableActorRef; private LocalActorRef _localActorRef; @@ -118,7 +118,7 @@ public async Task Setup() [Benchmark] public void ResolveChild() { - _cell.TryGetSingleChild(_getMessage.Name, out var child); + _cell.Child(_getMessage.Name); } [Benchmark] diff --git a/src/core/Akka/Actor/ActorCell.Children.cs b/src/core/Akka/Actor/ActorCell.Children.cs index db426b09e5f..ab59198342d 100644 --- a/src/core/Akka/Actor/ActorCell.Children.cs +++ b/src/core/Akka/Actor/ActorCell.Children.cs @@ -265,8 +265,7 @@ protected void SetTerminated() { get { - var terminating = ChildrenContainer as TerminatingChildrenContainer; - return terminating != null && terminating.Reason is SuspendReason.IWaitingForChildren; + return ChildrenContainer is TerminatingChildrenContainer terminating && terminating.Reason is SuspendReason.IWaitingForChildren; } } @@ -325,8 +324,7 @@ public bool TryGetChildStatsByName(string name, out IChildStats child) //This /// private bool TryGetChildRestartStatsByName(string name, out ChildRestartStats child) { - IChildStats stats; - if (ChildrenContainer.TryGetByName(name, out stats)) + if (ChildrenContainer.TryGetByName(name, out var stats)) { child = stats as ChildRestartStats; if (child != null) @@ -362,6 +360,8 @@ public IInternalActorRef GetSingleChild(string name) return TryGetSingleChild(name, out child) ? child : ActorRefs.Nobody; } + + /// /// TBD /// diff --git a/src/core/Akka/Actor/ActorCell.cs b/src/core/Akka/Actor/ActorCell.cs index 959faa67f75..1a3a32a7e3e 100644 --- a/src/core/Akka/Actor/ActorCell.cs +++ b/src/core/Akka/Actor/ActorCell.cs @@ -241,7 +241,10 @@ public void Init(bool sendSupervise, MailboxType mailboxType) IActorRef IActorContext.Child(string name) { - return TryGetSingleChild(name, out var child) ? child : ActorRefs.Nobody; + if (TryGetChildStatsByName(name, out var child) && child is ChildRestartStats s) + return s.Child; + + return ActorRefs.Nobody; } /// From 0aa71229a2a6c95812677e91ab5925b00e2c735a Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 15:59:04 -0500 Subject: [PATCH 02/16] modified `IActorRef.Child` API to accept a `IReadOnlyList` instead of `IEnumerable` --- .../CoreAPISpec.ApproveCore.approved.txt | 2 +- .../CoreAPISpec.ApproveRemote.approved.txt | 2 +- src/core/Akka.Remote/RemoteActorRef.cs | 10 +- src/core/Akka.Remote/RemoteSystemDaemon.cs | 10 +- src/core/Akka.TestKit/TestActorRefBase.cs | 2 +- src/core/Akka.TestKit/TestProbe.cs | 2 +- src/core/Akka/Actor/ActorRef.cs | 20 ++- src/core/Akka/Actor/ActorRefProvider.cs | 2 +- src/core/Akka/Actor/LocalActorRef.cs | 12 +- src/core/Akka/Actor/RepointableActorRef.cs | 15 +- .../Util/Internal/Collections/Iterator.cs | 141 ++++++++++++++++++ 11 files changed, 178 insertions(+), 40 deletions(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index e5c33308aec..80e99a33ab4 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1533,7 +1533,7 @@ namespace Akka.Actor public override Akka.Actor.ActorPath Path { get; } public override Akka.Actor.IActorRefProvider Provider { get; } public override Akka.Actor.ICell Underlying { get; } - public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name) { } + public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name) { } public override Akka.Actor.IInternalActorRef GetSingleChild(string name) { } public Akka.Actor.RepointableActorRef Initialize(bool async) { } protected virtual Akka.Actor.ActorCell NewCell() { } diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt index 0d3f12af45c..3b90d4f2deb 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt @@ -171,7 +171,7 @@ namespace Akka.Remote public override Akka.Actor.IInternalActorRef Parent { get; } public override Akka.Actor.ActorPath Path { get; } public override Akka.Actor.IActorRefProvider Provider { get; } - public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name) { } + public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name) { } public bool IsWatchIntercepted(Akka.Actor.IActorRef watchee, Akka.Actor.IActorRef watcher) { } public override void Restart(System.Exception cause) { } public override void Resume(System.Exception causedByFailure = null) { } diff --git a/src/core/Akka.Remote/RemoteActorRef.cs b/src/core/Akka.Remote/RemoteActorRef.cs index 098f9f6c846..5469940fbe7 100644 --- a/src/core/Akka.Remote/RemoteActorRef.cs +++ b/src/core/Akka.Remote/RemoteActorRef.cs @@ -12,6 +12,7 @@ using Akka.Annotations; using Akka.Dispatch.SysMsg; using Akka.Event; +using Akka.Util.Internal.Collections; namespace Akka.Remote { @@ -106,17 +107,16 @@ public RemoteActorRef(RemoteTransport remote, Address localAddressToUse, ActorPa /// The name. /// ActorRef. /// TBD - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { - var items = name.ToList(); - switch (items.FirstOrDefault()) + switch (name.FirstOrDefault()) { case null: return this; case "..": - return Parent.GetChild(items.Skip(1)); + return Parent.GetChild(new ListSlice(name, 1, name.Count-1)); default: - return new RemoteActorRef(Remote, LocalAddressToUse, Path / items, ActorRefs.Nobody, Props.None, Deploy.None); + return new RemoteActorRef(Remote, LocalAddressToUse, Path / name, ActorRefs.Nobody, Props.None, Deploy.None); } } diff --git a/src/core/Akka.Remote/RemoteSystemDaemon.cs b/src/core/Akka.Remote/RemoteSystemDaemon.cs index 1a820b62fb3..2e5fce15820 100644 --- a/src/core/Akka.Remote/RemoteSystemDaemon.cs +++ b/src/core/Akka.Remote/RemoteSystemDaemon.cs @@ -156,11 +156,9 @@ protected override void TellInternal(object message, IActorRef sender) } } - var t = Rec(ImmutableList.Empty); - var concatenatedChildNames = t.Item1; - var m = t.Item2; + var (concatenatedChildNames, m) = Rec(ImmutableList.Empty); - var child = GetChild(concatenatedChildNames); + var child = GetChild(concatenatedChildNames.ToList()); if (child.IsNobody()) { var emptyRef = new EmptyLocalActorRef(_system.Provider, @@ -302,7 +300,7 @@ private void HandleDaemonMsgCreate(DaemonMsgCreate message) /// /// The name. /// ActorRef. - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { var path = name.Join("/"); var n = 0; @@ -313,7 +311,7 @@ public override IActorRef GetChild(IEnumerable name) { if (uid != ActorCell.UndefinedUid && uid != child.Path.Uid) return Nobody.Instance; - return n == 0 ? child : child.GetChild(name.TakeRight(n)); + return n == 0 ? child : child.GetChild(name.TakeRight(n).ToList()); } var last = path.LastIndexOf("/", StringComparison.Ordinal); diff --git a/src/core/Akka.TestKit/TestActorRefBase.cs b/src/core/Akka.TestKit/TestActorRefBase.cs index b440c75f89e..a557d2545fc 100644 --- a/src/core/Akka.TestKit/TestActorRefBase.cs +++ b/src/core/Akka.TestKit/TestActorRefBase.cs @@ -258,7 +258,7 @@ ISurrogate ISurrogated.ToSurrogate(ActorSystem system) bool IInternalActorRef.IsTerminated { get { return _internalRef.IsTerminated; } } - IActorRef IInternalActorRef.GetChild(IEnumerable name) + IActorRef IInternalActorRef.GetChild(IReadOnlyList name) { return _internalRef.GetChild(name); } diff --git a/src/core/Akka.TestKit/TestProbe.cs b/src/core/Akka.TestKit/TestProbe.cs index 31fd374010c..ef586d69042 100644 --- a/src/core/Akka.TestKit/TestProbe.cs +++ b/src/core/Akka.TestKit/TestProbe.cs @@ -124,7 +124,7 @@ ISurrogate ISurrogated.ToSurrogate(ActorSystem system) bool IInternalActorRef.IsTerminated { get { return ((IInternalActorRef)TestActor).IsTerminated; } } - IActorRef IInternalActorRef.GetChild(IEnumerable name) + IActorRef IInternalActorRef.GetChild(IReadOnlyList name) { return ((IInternalActorRef)TestActor).GetChild(name); } diff --git a/src/core/Akka/Actor/ActorRef.cs b/src/core/Akka/Actor/ActorRef.cs index 959da6607cf..9826361931f 100644 --- a/src/core/Akka/Actor/ActorRef.cs +++ b/src/core/Akka/Actor/ActorRef.cs @@ -19,6 +19,7 @@ using Akka.Event; using Akka.Util; using Akka.Util.Internal; +using Akka.Util.Internal.Collections; namespace Akka.Actor { @@ -422,7 +423,7 @@ public interface IInternalActorRef : IActorRef, IActorRefScope /// /// The path elements. /// The , or if the requested path does not exist, returns . - IActorRef GetChild(IEnumerable name); + IActorRef GetChild(IReadOnlyList name); /// /// Resumes an actor if it has been suspended. @@ -481,7 +482,7 @@ public abstract class InternalActorRefBase : ActorRefBase, IInternalActorRef public abstract IActorRefProvider Provider { get; } /// - public abstract IActorRef GetChild(IEnumerable name); //TODO: Refactor this to use an IEnumerator instead as this will be faster instead of enumerating multiple times over name, as the implementations currently do. + public abstract IActorRef GetChild(IReadOnlyList name); //TODO: Refactor this to use an IEnumerator instead as this will be faster instead of enumerating multiple times over name, as the implementations currently do. /// public abstract void Resume(Exception causedByFailure = null); @@ -530,7 +531,7 @@ public override IInternalActorRef Parent } /// - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { if (name.All(string.IsNullOrEmpty)) return this; @@ -874,20 +875,17 @@ override def getChild(name: Iterator[String]): InternalActorRef = { /// /// TBD /// TBD - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { //Using enumerator to avoid multiple enumerations of name. - var enumerator = name.GetEnumerator(); - if (!enumerator.MoveNext()) - { - //name was empty + if (name.Count == 0) return this; - } - var firstName = enumerator.Current; + + var firstName = name[0]; if (string.IsNullOrEmpty(firstName)) return this; if (_children.TryGetValue(firstName, out var child)) - return child.GetChild(new Enumerable(enumerator)); + return child.GetChild(new ListSlice(name, 1, name.Count-1)); return ActorRefs.Nobody; } diff --git a/src/core/Akka/Actor/ActorRefProvider.cs b/src/core/Akka/Actor/ActorRefProvider.cs index 0c9cd6dd3f1..aa6e21adb95 100644 --- a/src/core/Akka/Actor/ActorRefProvider.cs +++ b/src/core/Akka/Actor/ActorRefProvider.cs @@ -462,7 +462,7 @@ public IActorRef ResolveActorRef(ActorPath path) /// TBD /// TBD /// TBD - internal IInternalActorRef ResolveActorRef(IInternalActorRef actorRef, IReadOnlyCollection pathElements) + internal IInternalActorRef ResolveActorRef(IInternalActorRef actorRef, IReadOnlyList pathElements) { if (pathElements.Count == 0) { diff --git a/src/core/Akka/Actor/LocalActorRef.cs b/src/core/Akka/Actor/LocalActorRef.cs index 10e4f298b46..0fe14efaddf 100644 --- a/src/core/Akka/Actor/LocalActorRef.cs +++ b/src/core/Akka/Actor/LocalActorRef.cs @@ -12,6 +12,7 @@ using Akka.Dispatch; using Akka.Dispatch.SysMsg; using Akka.Util.Internal; +using Akka.Util.Internal.Collections; namespace Akka.Actor { @@ -211,14 +212,13 @@ public override IInternalActorRef GetSingleChild(string name) } /// - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { - var current = (IActorRef)this; + IActorRef current = this; int index = 0; - foreach (string element in name) + foreach (var element in name) { - var currentLocalActorRef = current as LocalActorRef; - if (currentLocalActorRef != null) + if (current is LocalActorRef currentLocalActorRef) { switch (element) { @@ -237,7 +237,7 @@ public override IActorRef GetChild(IEnumerable name) //Current is not a LocalActorRef if (current != null) { - var rest = name.Skip(index).ToList(); + var rest = new ListSlice(name, 1, name.Count-1); return current.AsInstanceOf().GetChild(rest); } throw new NotSupportedException("Bug, we should not get here"); diff --git a/src/core/Akka/Actor/RepointableActorRef.cs b/src/core/Akka/Actor/RepointableActorRef.cs index 6933f23da21..ef9dcb108ec 100644 --- a/src/core/Akka/Actor/RepointableActorRef.cs +++ b/src/core/Akka/Actor/RepointableActorRef.cs @@ -15,6 +15,7 @@ using Akka.Dispatch.SysMsg; using Akka.Event; using Akka.Pattern; +using Akka.Util.Internal.Collections; namespace Akka.Actor { @@ -278,17 +279,17 @@ protected override void TellInternal(object message, IActorRef sender) /// /// TBD /// TBD - public override IActorRef GetChild(IEnumerable name) + public override IActorRef GetChild(IReadOnlyList name) { - var current = (IActorRef)this; - if (!name.Any()) return current; + IActorRef current = this; + if (name.Count == 0) return current; - var next = name.FirstOrDefault() ?? ""; + var next = name[0]; switch (next) { case "..": - return Parent.GetChild(name.Skip(1)); + return Parent.GetChild(new ListSlice(name, 1, name.Count-1)); case "": return ActorRefs.Nobody; default: @@ -297,8 +298,8 @@ public override IActorRef GetChild(IEnumerable name) { if (stats is ChildRestartStats crs && (uid == ActorCell.UndefinedUid || uid == crs.Uid)) { - if (name.Skip(1).Any()) - return crs.Child.GetChild(name.Skip(1)); + if (name.Count > 1) + return crs.Child.GetChild(new ListSlice(name, 1, name.Count-1)); else return crs.Child; } diff --git a/src/core/Akka/Util/Internal/Collections/Iterator.cs b/src/core/Akka/Util/Internal/Collections/Iterator.cs index bafba0c7b5f..84ef016579b 100644 --- a/src/core/Akka/Util/Internal/Collections/Iterator.cs +++ b/src/core/Akka/Util/Internal/Collections/Iterator.cs @@ -5,11 +5,152 @@ // //----------------------------------------------------------------------- +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; namespace Akka.Util.Internal.Collections { + internal struct ListSlice : IList, IReadOnlyList + { + private sealed class SliceEnumerator : IEnumerator + { + private IReadOnlyList _array; + private int _start; + private int _end; + private int _current; + + public SliceEnumerator(ListSlice array) + { + _array = array._array; + _start = array.Offset; + _end = _start + array.Count; + _current = _start - 1; + } + + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return (_current < _end); + } + return false; + } + + public void Reset() + { + _current = _start - 1; + } + + public T Current + { + get + { + if (_current < _start) throw new InvalidOperationException("Enumeration not started."); + if (_current >= _end) throw new InvalidOperationException("Enumeration ended."); + return _array[_current]; + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + + } + } + + private readonly IReadOnlyList _array; + + public ListSlice(IReadOnlyList array) + { + + if (array == null) + throw new ArgumentNullException(nameof(array)); + + _array = array; + Offset = 0; + Count = array.Count; + } + + public ListSlice(IReadOnlyList array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be below zero."); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be below zero."); + + _array = array; + Offset = offset; + Count = count; + } + + public int Offset { get; } + + public IEnumerator GetEnumerator() + { + return new SliceEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(T item) + { + throw new System.NotImplementedException(); + } + + public void Clear() + { + throw new System.NotImplementedException(); + } + + public bool Contains(T item) + { + throw new System.NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new System.NotImplementedException(); + } + + public bool Remove(T item) + { + throw new System.NotImplementedException(); + } + + public int Count { get; } + + public bool IsReadOnly => true; + public int IndexOf(T item) + { + throw new System.NotImplementedException(); + } + + public void Insert(int index, T item) + { + throw new System.NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new System.NotImplementedException(); + } + + public T this[int index] + { + get => _array.ElementAt(Offset + index); + set => throw new System.NotImplementedException(); + } + } + internal struct Iterator { private readonly IList _enumerator; From bf6e1624f61966db7e48a4a3774d94a82c153e1d Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 16:50:08 -0500 Subject: [PATCH 03/16] fixed a bug with low-level resolution --- src/core/Akka/Actor/LocalActorRef.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka/Actor/LocalActorRef.cs b/src/core/Akka/Actor/LocalActorRef.cs index 0fe14efaddf..e874e301c9b 100644 --- a/src/core/Akka/Actor/LocalActorRef.cs +++ b/src/core/Akka/Actor/LocalActorRef.cs @@ -237,7 +237,7 @@ public override IActorRef GetChild(IReadOnlyList name) //Current is not a LocalActorRef if (current != null) { - var rest = new ListSlice(name, 1, name.Count-1); + var rest = new ListSlice(name, index, name.Count-index); return current.AsInstanceOf().GetChild(rest); } throw new NotSupportedException("Bug, we should not get here"); From fdac3b9616694e9ad2025155344099a015e9a7a9 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 17:08:46 -0500 Subject: [PATCH 04/16] approved core API changes --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 80e99a33ab4..771d2e1e1cb 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1317,7 +1317,7 @@ namespace Akka.Actor [System.ObsoleteAttribute("Use Context.Watch and Receive [1.1.0]")] public override bool IsTerminated { get; } public override Akka.Actor.IInternalActorRef Parent { get; } - public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name) { } + public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name) { } public override void Restart(System.Exception cause) { } public override void Resume(System.Exception causedByFailure = null) { } public override void SendSystemMessage(Akka.Dispatch.SysMsg.ISystemMessage message) { } From bc91ad8ad0dbcadb40f88801db65cb76f6bef7e2 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 17:09:11 -0500 Subject: [PATCH 05/16] approved again.... --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 771d2e1e1cb..9ecf1839cd5 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1245,7 +1245,7 @@ namespace Akka.Actor protected Akka.Actor.IInternalActorRef Supervisor { get; } protected Akka.Actor.ActorSystem System { get; } public override Akka.Actor.ICell Underlying { get; } - public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name) { } + public override Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name) { } public override Akka.Actor.IInternalActorRef GetSingleChild(string name) { } protected virtual Akka.Actor.ActorCell NewActorCell(Akka.Actor.Internal.ActorSystemImpl system, Akka.Actor.IInternalActorRef self, Akka.Actor.Props props, Akka.Dispatch.MessageDispatcher dispatcher, Akka.Actor.IInternalActorRef supervisor) { } public override void Restart(System.Exception cause) { } From d754014a93d210e6b976441f37380c961705ab23 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 17:13:21 -0500 Subject: [PATCH 06/16] fixed - was missing one approval --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 9ecf1839cd5..a47d7413bb0 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1081,7 +1081,7 @@ namespace Akka.Actor bool IsTerminated { get; } Akka.Actor.IInternalActorRef Parent { get; } Akka.Actor.IActorRefProvider Provider { get; } - Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name); + Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name); void Restart(System.Exception cause); void Resume(System.Exception causedByFailure = null); [System.ObsoleteAttribute("Use SendSystemMessage(message) [1.1.0]")] @@ -1202,7 +1202,7 @@ namespace Akka.Actor public abstract bool IsTerminated { get; } public abstract Akka.Actor.IInternalActorRef Parent { get; } public abstract Akka.Actor.IActorRefProvider Provider { get; } - public abstract Akka.Actor.IActorRef GetChild(System.Collections.Generic.IEnumerable name); + public abstract Akka.Actor.IActorRef GetChild(System.Collections.Generic.IReadOnlyList name); public abstract void Restart(System.Exception cause); public abstract void Resume(System.Exception causedByFailure = null); [System.ObsoleteAttribute("Use SendSystemMessage(message) instead [1.1.0]")] From 3910d11ccbf127865c690ca8752ec76f53a65072 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 17:46:48 -0500 Subject: [PATCH 07/16] added extension method to simplify copying --- src/core/Akka.Remote/RemoteActorRef.cs | 2 +- src/core/Akka/Actor/ActorCell.Children.cs | 3 +- src/core/Akka/Actor/ActorRef.cs | 2 +- src/core/Akka/Actor/LocalActorRef.cs | 2 +- .../Util/Internal/Collections/Iterator.cs | 141 -------------- .../Util/Internal/Collections/ListSlice.cs | 178 ++++++++++++++++++ 6 files changed, 182 insertions(+), 146 deletions(-) create mode 100644 src/core/Akka/Util/Internal/Collections/ListSlice.cs diff --git a/src/core/Akka.Remote/RemoteActorRef.cs b/src/core/Akka.Remote/RemoteActorRef.cs index 5469940fbe7..976b6956ec5 100644 --- a/src/core/Akka.Remote/RemoteActorRef.cs +++ b/src/core/Akka.Remote/RemoteActorRef.cs @@ -114,7 +114,7 @@ public override IActorRef GetChild(IReadOnlyList name) case null: return this; case "..": - return Parent.GetChild(new ListSlice(name, 1, name.Count-1)); + return Parent.GetChild(name.NoCopySlice(1)); default: return new RemoteActorRef(Remote, LocalAddressToUse, Path / name, ActorRefs.Nobody, Props.None, Deploy.None); } diff --git a/src/core/Akka/Actor/ActorCell.Children.cs b/src/core/Akka/Actor/ActorCell.Children.cs index ab59198342d..d33f870beae 100644 --- a/src/core/Akka/Actor/ActorCell.Children.cs +++ b/src/core/Akka/Actor/ActorCell.Children.cs @@ -356,8 +356,7 @@ protected bool TryGetChildStatsByRef(IActorRef actor, out ChildRestartStats chil [Obsolete("Use TryGetSingleChild [0.7.1]")] public IInternalActorRef GetSingleChild(string name) { - IInternalActorRef child; - return TryGetSingleChild(name, out child) ? child : ActorRefs.Nobody; + return TryGetSingleChild(name, out var child) ? child : ActorRefs.Nobody; } diff --git a/src/core/Akka/Actor/ActorRef.cs b/src/core/Akka/Actor/ActorRef.cs index 9826361931f..abe28111db3 100644 --- a/src/core/Akka/Actor/ActorRef.cs +++ b/src/core/Akka/Actor/ActorRef.cs @@ -885,7 +885,7 @@ public override IActorRef GetChild(IReadOnlyList name) if (string.IsNullOrEmpty(firstName)) return this; if (_children.TryGetValue(firstName, out var child)) - return child.GetChild(new ListSlice(name, 1, name.Count-1)); + return child.GetChild(name.NoCopySlice(1)); return ActorRefs.Nobody; } diff --git a/src/core/Akka/Actor/LocalActorRef.cs b/src/core/Akka/Actor/LocalActorRef.cs index e874e301c9b..439177977fd 100644 --- a/src/core/Akka/Actor/LocalActorRef.cs +++ b/src/core/Akka/Actor/LocalActorRef.cs @@ -237,7 +237,7 @@ public override IActorRef GetChild(IReadOnlyList name) //Current is not a LocalActorRef if (current != null) { - var rest = new ListSlice(name, index, name.Count-index); + var rest = name.NoCopySlice(index); return current.AsInstanceOf().GetChild(rest); } throw new NotSupportedException("Bug, we should not get here"); diff --git a/src/core/Akka/Util/Internal/Collections/Iterator.cs b/src/core/Akka/Util/Internal/Collections/Iterator.cs index 84ef016579b..bafba0c7b5f 100644 --- a/src/core/Akka/Util/Internal/Collections/Iterator.cs +++ b/src/core/Akka/Util/Internal/Collections/Iterator.cs @@ -5,152 +5,11 @@ // //----------------------------------------------------------------------- -using System; -using System.Collections; using System.Collections.Generic; using System.Linq; namespace Akka.Util.Internal.Collections { - internal struct ListSlice : IList, IReadOnlyList - { - private sealed class SliceEnumerator : IEnumerator - { - private IReadOnlyList _array; - private int _start; - private int _end; - private int _current; - - public SliceEnumerator(ListSlice array) - { - _array = array._array; - _start = array.Offset; - _end = _start + array.Count; - _current = _start - 1; - } - - public bool MoveNext() - { - if (_current < _end) - { - _current++; - return (_current < _end); - } - return false; - } - - public void Reset() - { - _current = _start - 1; - } - - public T Current - { - get - { - if (_current < _start) throw new InvalidOperationException("Enumeration not started."); - if (_current >= _end) throw new InvalidOperationException("Enumeration ended."); - return _array[_current]; - } - } - - object IEnumerator.Current => Current; - - public void Dispose() - { - - } - } - - private readonly IReadOnlyList _array; - - public ListSlice(IReadOnlyList array) - { - - if (array == null) - throw new ArgumentNullException(nameof(array)); - - _array = array; - Offset = 0; - Count = array.Count; - } - - public ListSlice(IReadOnlyList array, int offset, int count) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be below zero."); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), "Cannot be below zero."); - - _array = array; - Offset = offset; - Count = count; - } - - public int Offset { get; } - - public IEnumerator GetEnumerator() - { - return new SliceEnumerator(this); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void Add(T item) - { - throw new System.NotImplementedException(); - } - - public void Clear() - { - throw new System.NotImplementedException(); - } - - public bool Contains(T item) - { - throw new System.NotImplementedException(); - } - - public void CopyTo(T[] array, int arrayIndex) - { - throw new System.NotImplementedException(); - } - - public bool Remove(T item) - { - throw new System.NotImplementedException(); - } - - public int Count { get; } - - public bool IsReadOnly => true; - public int IndexOf(T item) - { - throw new System.NotImplementedException(); - } - - public void Insert(int index, T item) - { - throw new System.NotImplementedException(); - } - - public void RemoveAt(int index) - { - throw new System.NotImplementedException(); - } - - public T this[int index] - { - get => _array.ElementAt(Offset + index); - set => throw new System.NotImplementedException(); - } - } - internal struct Iterator { private readonly IList _enumerator; diff --git a/src/core/Akka/Util/Internal/Collections/ListSlice.cs b/src/core/Akka/Util/Internal/Collections/ListSlice.cs new file mode 100644 index 00000000000..f49a4b73b51 --- /dev/null +++ b/src/core/Akka/Util/Internal/Collections/ListSlice.cs @@ -0,0 +1,178 @@ +// //----------------------------------------------------------------------- +// // +// // Copyright (C) 2009-2021 Lightbend Inc. +// // Copyright (C) 2013-2021 .NET Foundation +// // +// //----------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Akka.Util.Internal.Collections +{ + internal static class SliceExtensions + { + public static IReadOnlyList NoCopySlice(this IReadOnlyList list, int offset) + { + switch (list) + { + // slice of a slice + case ListSlice slice: + { + return new ListSlice(slice.Array, slice.Offset + offset, slice.Array.Count - slice.Offset - offset); + } + default: + { + return new ListSlice(list, offset, list.Count - offset); + } + } + } + } + + /// + /// but for + /// + /// + internal struct ListSlice : IList, IReadOnlyList + { + private sealed class SliceEnumerator : IEnumerator + { + private readonly IReadOnlyList _array; + private readonly int _start; + private readonly int _end; + private int _current; + + public SliceEnumerator(ListSlice array) + { + _array = array._array; + _start = array.Offset; + _end = _start + array.Count; + _current = _start - 1; + } + + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return (_current < _end); + } + return false; + } + + public void Reset() + { + _current = _start - 1; + } + + public T Current + { + get + { + if (_current < _start) throw new InvalidOperationException("Enumeration not started."); + if (_current >= _end) throw new InvalidOperationException("Enumeration ended."); + return _array[_current]; + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + + } + } + + private readonly IReadOnlyList _array; + + public ListSlice(IReadOnlyList array) + { + + if (array == null) + throw new ArgumentNullException(nameof(array)); + + _array = array; + Offset = 0; + Count = array.Count; + } + + public ListSlice(IReadOnlyList array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be below zero."); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be below zero."); + + _array = array; + Offset = offset; + Count = count; + } + + public int Offset { get; } + + public IReadOnlyList Array => _array; + + public IEnumerator GetEnumerator() + { + return new SliceEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(T item) + { + throw new System.NotImplementedException(); + } + + public void Clear() + { + throw new System.NotImplementedException(); + } + + public bool Contains(T item) + { + throw new System.NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new System.NotImplementedException(); + } + + public bool Remove(T item) + { + throw new System.NotImplementedException(); + } + + public int Count { get; } + + public bool IsReadOnly => true; + public int IndexOf(T item) + { + throw new System.NotImplementedException(); + } + + public void Insert(int index, T item) + { + throw new System.NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new System.NotImplementedException(); + } + + public T this[int index] + { + get => _array.ElementAt(Offset + index); + set => throw new System.NotImplementedException(); + } + } +} \ No newline at end of file From f8f5342215a722fa04299f44a4f90e29eed0e7d8 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 17:49:16 -0500 Subject: [PATCH 08/16] cleaned up RepointableActorRef --- src/core/Akka/Actor/RepointableActorRef.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/Akka/Actor/RepointableActorRef.cs b/src/core/Akka/Actor/RepointableActorRef.cs index ef9dcb108ec..31ccb2f872e 100644 --- a/src/core/Akka/Actor/RepointableActorRef.cs +++ b/src/core/Akka/Actor/RepointableActorRef.cs @@ -281,15 +281,14 @@ protected override void TellInternal(object message, IActorRef sender) /// TBD public override IActorRef GetChild(IReadOnlyList name) { - IActorRef current = this; - if (name.Count == 0) return current; + if (name.Count == 0) return this; var next = name[0]; switch (next) { case "..": - return Parent.GetChild(new ListSlice(name, 1, name.Count-1)); + return Parent.GetChild(name.NoCopySlice(1)); case "": return ActorRefs.Nobody; default: @@ -299,7 +298,7 @@ public override IActorRef GetChild(IReadOnlyList name) if (stats is ChildRestartStats crs && (uid == ActorCell.UndefinedUid || uid == crs.Uid)) { if (name.Count > 1) - return crs.Child.GetChild(new ListSlice(name, 1, name.Count-1)); + return crs.Child.GetChild(name.NoCopySlice(1)); else return crs.Child; } From 3197d0c465d944a417eb6802f14033895f36097c Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 18:34:01 -0500 Subject: [PATCH 09/16] cleaned up branching --- src/core/Akka/Actor/LocalActorRef.cs | 43 +++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/core/Akka/Actor/LocalActorRef.cs b/src/core/Akka/Actor/LocalActorRef.cs index 439177977fd..244bec0efbc 100644 --- a/src/core/Akka/Actor/LocalActorRef.cs +++ b/src/core/Akka/Actor/LocalActorRef.cs @@ -53,7 +53,8 @@ public class LocalActorRef : ActorRefWithCell, ILocalRef /// TBD /// TBD /// TBD - public LocalActorRef(ActorSystemImpl system, Props props, MessageDispatcher dispatcher, MailboxType mailboxType, IInternalActorRef supervisor, ActorPath path) + public LocalActorRef(ActorSystemImpl system, Props props, MessageDispatcher dispatcher, MailboxType mailboxType, + IInternalActorRef supervisor, ActorPath path) { _system = system; _props = props; @@ -62,18 +63,19 @@ public LocalActorRef(ActorSystemImpl system, Props props, MessageDispatcher disp _supervisor = supervisor; _path = path; - /* - * Safe publication of this class’s fields is guaranteed by Mailbox.SetActor() - * which is called indirectly from ActorCell.init() (if you’re wondering why - * this is at all important, remember that under the CLR readonly fields are only - * frozen at the _end_ of the constructor, but we are publishing "this" before - * that is reached). - * This means that the result of NewActorCell needs to be written to the field - * _cell before we call init and start, since we can start using "this" - * object from another thread as soon as we run init. - */ + /* + * Safe publication of this class’s fields is guaranteed by Mailbox.SetActor() + * which is called indirectly from ActorCell.init() (if you’re wondering why + * this is at all important, remember that under the CLR readonly fields are only + * frozen at the _end_ of the constructor, but we are publishing "this" before + * that is reached). + * This means that the result of NewActorCell needs to be written to the field + * _cell before we call init and start, since we can start using "this" + * object from another thread as soon as we run init. + */ // ReSharper disable once VirtualMemberCallInConstructor - _cell = NewActorCell(_system, this, _props, _dispatcher, _supervisor); // _cell needs to be assigned before Init is called. + _cell = NewActorCell(_system, this, _props, _dispatcher, + _supervisor); // _cell needs to be assigned before Init is called. _cell.Init(true, MailboxType); } @@ -232,20 +234,21 @@ public override IActorRef GetChild(IReadOnlyList name) break; } } - else + else if (current is IInternalActorRef internalActorRef) { //Current is not a LocalActorRef - if (current != null) - { - var rest = name.NoCopySlice(index); - return current.AsInstanceOf().GetChild(rest); - } + var rest = name.NoCopySlice(index); + return internalActorRef.GetChild(rest); + } + else // not a LocalActorRef or an IInternalActorRef + { throw new NotSupportedException("Bug, we should not get here"); } + index++; } + return current; } } -} - +} \ No newline at end of file From 6b590d7190cc5d0ae9143166838b3906ab29f339 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 21:28:44 -0500 Subject: [PATCH 10/16] implemented CopyTo method for debugging --- src/core/Akka/Util/Internal/Collections/ListSlice.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/Akka/Util/Internal/Collections/ListSlice.cs b/src/core/Akka/Util/Internal/Collections/ListSlice.cs index f49a4b73b51..83cc591b957 100644 --- a/src/core/Akka/Util/Internal/Collections/ListSlice.cs +++ b/src/core/Akka/Util/Internal/Collections/ListSlice.cs @@ -143,7 +143,11 @@ public bool Contains(T item) public void CopyTo(T[] array, int arrayIndex) { - throw new System.NotImplementedException(); + var n = 0; + foreach (var i in _array.Skip(Offset).Take(Count)) + { + array[arrayIndex + n++] = i; + } } public bool Remove(T item) From 8cce9b70cb0bfa2f406b547b37ef5e902de7c57d Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 21:38:51 -0500 Subject: [PATCH 11/16] added child-resolve program --- src/Akka.sln | 15 +++ .../Akka.ChildResolve.csproj | 19 ++++ src/benchmark/Akka.ChildResolve/Program.cs | 107 ++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj create mode 100644 src/benchmark/Akka.ChildResolve/Program.cs diff --git a/src/Akka.sln b/src/Akka.sln index bf0782f7236..f549c34eeec 100644 --- a/src/Akka.sln +++ b/src/Akka.sln @@ -248,6 +248,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DDataStressTest", "examples EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Cluster.Benchmarks", "benchmark\Akka.Cluster.Benchmarks\Akka.Cluster.Benchmarks.csproj", "{3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.ChildResolve", "benchmark\Akka.ChildResolve\Akka.ChildResolve.csproj", "{1AF11505-8B49-40AF-8059-E214B775AC94}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1151,6 +1153,18 @@ Global {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x64.Build.0 = Release|Any CPU {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x86.ActiveCfg = Release|Any CPU {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x86.Build.0 = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x64.ActiveCfg = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x64.Build.0 = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x86.ActiveCfg = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x86.Build.0 = Debug|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|Any CPU.Build.0 = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x64.ActiveCfg = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x64.Build.0 = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x86.ActiveCfg = Release|Any CPU + {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1260,6 +1274,7 @@ Global {2E4B9584-42CC-4D17-B719-9F462B16C94D} = {73108242-625A-4D7B-AA09-63375DBAE464} {44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50} = {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7} {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B} = {73108242-625A-4D7B-AA09-63375DBAE464} + {1AF11505-8B49-40AF-8059-E214B775AC94} = {73108242-625A-4D7B-AA09-63375DBAE464} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164} diff --git a/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj b/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj new file mode 100644 index 00000000000..489a1521007 --- /dev/null +++ b/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/benchmark/Akka.ChildResolve/Program.cs b/src/benchmark/Akka.ChildResolve/Program.cs new file mode 100644 index 00000000000..0c0b2cd36d0 --- /dev/null +++ b/src/benchmark/Akka.ChildResolve/Program.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Util.Internal; + +namespace Akka.ChildResolve +{ + public sealed class Child : UntypedActor + { + protected override void OnReceive(object message) + { + } + + protected override void PreStart() + { + if (Self.Path.Name.Length > 1) + { + // recursively create children using the previous name segments + var nextName = new string(Self.Path.Name.Skip(1).ToArray()); + Context.ActorOf(Props.Create(() => new Child()), nextName); + } + } + } + + public sealed class ActorWithChild : UntypedActor + { + public sealed class Get + { + public Get(string name) + { + Name = name; + } + + public string Name { get; } + } + + public sealed class Create + { + public Create(string name) + { + Name = name; + } + + public string Name { get; } + } + + protected override void OnReceive(object message) + { + switch (message) + { + case Get g: + { + var child = Context.Child(g.Name); + Sender.Tell(child); + break; + } + case Create c: + { + var child = Context.ActorOf(Props.Create(() => new Child()), c.Name); + Sender.Tell(child); + break; + } + default: + Unhandled(message); + break; + } + } + } + + class Program + { + static async Task Main(string[] args) + { + TimeSpan _timeout; + ActorSystem _system; + IActorRef _parentActor; + + ActorWithChild.Get _getMessage = new ActorWithChild.Get("food"); + ActorWithChild.Create _createMessage = new ActorWithChild.Create("food"); + + IActorContext _cell; + RepointableActorRef _repointableActorRef; + LocalActorRef _localActorRef; + + List _rpChildQueryPath = new List() { "food", "ood", "od" }; + List _lclChildQueryPath = new List() { "ood", "od", "d" }; + + + _timeout = TimeSpan.FromMinutes(1); + _system = ActorSystem.Create("system"); + _parentActor = _system.ActorOf(Props.Create(() => new ActorWithChild()), "parent"); + _localActorRef = (LocalActorRef)await _parentActor.Ask(_createMessage, _timeout); + + _cell = _parentActor.AsInstanceOf().Underlying.AsInstanceOf(); + _repointableActorRef = (RepointableActorRef)_parentActor; + + + foreach (var i in Enumerable.Range(0, 1_000_000)) + _cell.Child(_getMessage.Name); + + foreach (var i in Enumerable.Range(0, 100_000_000)) + _repointableActorRef.GetChild(_rpChildQueryPath); + } + } +} \ No newline at end of file From 16e33c8c502c2e0fe0f8e15e6f2efb886be763b0 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 21:39:19 -0500 Subject: [PATCH 12/16] removed expensive allocation from `MinimalActorRef.GetChild` --- src/core/Akka/Actor/ActorRef.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka/Actor/ActorRef.cs b/src/core/Akka/Actor/ActorRef.cs index abe28111db3..ca783f4ad5f 100644 --- a/src/core/Akka/Actor/ActorRef.cs +++ b/src/core/Akka/Actor/ActorRef.cs @@ -533,7 +533,7 @@ public override IInternalActorRef Parent /// public override IActorRef GetChild(IReadOnlyList name) { - if (name.All(string.IsNullOrEmpty)) + if (name.All(x => string.IsNullOrEmpty(x))) return this; return ActorRefs.Nobody; } From bc661272eacd1407868e49f0e4d24e49654839db Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 1 Sep 2021 21:52:22 -0500 Subject: [PATCH 13/16] Revert "added child-resolve program" This reverts commit 8cce9b70cb0bfa2f406b547b37ef5e902de7c57d. --- src/Akka.sln | 15 --- .../Akka.ChildResolve.csproj | 19 ---- src/benchmark/Akka.ChildResolve/Program.cs | 107 ------------------ 3 files changed, 141 deletions(-) delete mode 100644 src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj delete mode 100644 src/benchmark/Akka.ChildResolve/Program.cs diff --git a/src/Akka.sln b/src/Akka.sln index f549c34eeec..bf0782f7236 100644 --- a/src/Akka.sln +++ b/src/Akka.sln @@ -248,8 +248,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DDataStressTest", "examples EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Cluster.Benchmarks", "benchmark\Akka.Cluster.Benchmarks\Akka.Cluster.Benchmarks.csproj", "{3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.ChildResolve", "benchmark\Akka.ChildResolve\Akka.ChildResolve.csproj", "{1AF11505-8B49-40AF-8059-E214B775AC94}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1153,18 +1151,6 @@ Global {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x64.Build.0 = Release|Any CPU {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x86.ActiveCfg = Release|Any CPU {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B}.Release|x86.Build.0 = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x64.ActiveCfg = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x64.Build.0 = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x86.ActiveCfg = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Debug|x86.Build.0 = Debug|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|Any CPU.Build.0 = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x64.ActiveCfg = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x64.Build.0 = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x86.ActiveCfg = Release|Any CPU - {1AF11505-8B49-40AF-8059-E214B775AC94}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1274,7 +1260,6 @@ Global {2E4B9584-42CC-4D17-B719-9F462B16C94D} = {73108242-625A-4D7B-AA09-63375DBAE464} {44B3DDD6-6103-4E8F-8AC2-0F4BA3CF6B50} = {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7} {3CEBB0AE-6A88-4C32-A1D3-A8FB1E7E236B} = {73108242-625A-4D7B-AA09-63375DBAE464} - {1AF11505-8B49-40AF-8059-E214B775AC94} = {73108242-625A-4D7B-AA09-63375DBAE464} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164} diff --git a/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj b/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj deleted file mode 100644 index 489a1521007..00000000000 --- a/src/benchmark/Akka.ChildResolve/Akka.ChildResolve.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - netcoreapp3.1 - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/src/benchmark/Akka.ChildResolve/Program.cs b/src/benchmark/Akka.ChildResolve/Program.cs deleted file mode 100644 index 0c0b2cd36d0..00000000000 --- a/src/benchmark/Akka.ChildResolve/Program.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Akka.Actor; -using Akka.Util.Internal; - -namespace Akka.ChildResolve -{ - public sealed class Child : UntypedActor - { - protected override void OnReceive(object message) - { - } - - protected override void PreStart() - { - if (Self.Path.Name.Length > 1) - { - // recursively create children using the previous name segments - var nextName = new string(Self.Path.Name.Skip(1).ToArray()); - Context.ActorOf(Props.Create(() => new Child()), nextName); - } - } - } - - public sealed class ActorWithChild : UntypedActor - { - public sealed class Get - { - public Get(string name) - { - Name = name; - } - - public string Name { get; } - } - - public sealed class Create - { - public Create(string name) - { - Name = name; - } - - public string Name { get; } - } - - protected override void OnReceive(object message) - { - switch (message) - { - case Get g: - { - var child = Context.Child(g.Name); - Sender.Tell(child); - break; - } - case Create c: - { - var child = Context.ActorOf(Props.Create(() => new Child()), c.Name); - Sender.Tell(child); - break; - } - default: - Unhandled(message); - break; - } - } - } - - class Program - { - static async Task Main(string[] args) - { - TimeSpan _timeout; - ActorSystem _system; - IActorRef _parentActor; - - ActorWithChild.Get _getMessage = new ActorWithChild.Get("food"); - ActorWithChild.Create _createMessage = new ActorWithChild.Create("food"); - - IActorContext _cell; - RepointableActorRef _repointableActorRef; - LocalActorRef _localActorRef; - - List _rpChildQueryPath = new List() { "food", "ood", "od" }; - List _lclChildQueryPath = new List() { "ood", "od", "d" }; - - - _timeout = TimeSpan.FromMinutes(1); - _system = ActorSystem.Create("system"); - _parentActor = _system.ActorOf(Props.Create(() => new ActorWithChild()), "parent"); - _localActorRef = (LocalActorRef)await _parentActor.Ask(_createMessage, _timeout); - - _cell = _parentActor.AsInstanceOf().Underlying.AsInstanceOf(); - _repointableActorRef = (RepointableActorRef)_parentActor; - - - foreach (var i in Enumerable.Range(0, 1_000_000)) - _cell.Child(_getMessage.Name); - - foreach (var i in Enumerable.Range(0, 100_000_000)) - _repointableActorRef.GetChild(_rpChildQueryPath); - } - } -} \ No newline at end of file From 9f8b85208954a58bcc6299a5636ed9c403bc4ad5 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Thu, 2 Sep 2021 10:03:19 -0500 Subject: [PATCH 14/16] fixed indexer operation --- src/core/Akka/Util/Internal/Collections/ListSlice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka/Util/Internal/Collections/ListSlice.cs b/src/core/Akka/Util/Internal/Collections/ListSlice.cs index 83cc591b957..5614b6a05b7 100644 --- a/src/core/Akka/Util/Internal/Collections/ListSlice.cs +++ b/src/core/Akka/Util/Internal/Collections/ListSlice.cs @@ -175,7 +175,7 @@ public void RemoveAt(int index) public T this[int index] { - get => _array.ElementAt(Offset + index); + get => _array[Offset + index]; set => throw new System.NotImplementedException(); } } From 83224bebbefed555fab2e2c057b409beb6f4c4a9 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Thu, 2 Sep 2021 11:10:13 -0500 Subject: [PATCH 15/16] simplified branching inside `TryGetSingleChild` --- src/core/Akka/Actor/ActorCell.Children.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/Akka/Actor/ActorCell.Children.cs b/src/core/Akka/Actor/ActorCell.Children.cs index d33f870beae..cc08f7ca42b 100644 --- a/src/core/Akka/Actor/ActorCell.Children.cs +++ b/src/core/Akka/Actor/ActorCell.Children.cs @@ -372,12 +372,13 @@ public bool TryGetSingleChild(string name, out IInternalActorRef child) if (name.IndexOf('#') < 0) { // optimization for the non-uid case - if (TryGetChildRestartStatsByName(name, out var stats)) + if (ChildrenContainer.TryGetByName(name, out var stats) && stats is ChildRestartStats r) { - child = stats.Child; + child = r.Child; return true; } - else if (TryGetFunctionRef(name, out var functionRef)) + + if (TryGetFunctionRef(name, out var functionRef)) { child = functionRef; return true; @@ -386,15 +387,13 @@ public bool TryGetSingleChild(string name, out IInternalActorRef child) else { var (s, uid) = GetNameAndUid(name); - if (TryGetChildRestartStatsByName(s, out var stats)) + if (TryGetChildRestartStatsByName(s, out var stats) && (uid == ActorCell.UndefinedUid || uid == stats.Uid)) { - if (uid == ActorCell.UndefinedUid || uid == stats.Uid) - { - child = stats.Child; + child = stats.Child; return true; - } } - else if (TryGetFunctionRef(s, uid, out var functionRef)) + + if (TryGetFunctionRef(s, uid, out var functionRef)) { child = functionRef; return true; From 60b3537e6e0aae6088659bbde4e338c5da7a280e Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Thu, 2 Sep 2021 11:33:11 -0500 Subject: [PATCH 16/16] removed redundant TryOut methods --- .../CoreAPISpec.ApproveCore.approved.txt | 2 -- src/core/Akka/Actor/ActorCell.Children.cs | 31 ++++--------------- src/core/Akka/Actor/ActorCell.cs | 2 +- src/core/Akka/Actor/LocalActorRef.cs | 3 +- 4 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index a47d7413bb0..ef20cd29aba 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -93,7 +93,6 @@ namespace Akka.Actor public System.Collections.Generic.IEnumerable GetChildren() { } public static Akka.Actor.IActorRef GetCurrentSelfOrNoSender() { } public static Akka.Actor.IActorRef GetCurrentSenderOrNoSender() { } - [System.ObsoleteAttribute("Use TryGetSingleChild [0.7.1]")] public Akka.Actor.IInternalActorRef GetSingleChild(string name) { } public void Init(bool sendSupervise, Akka.Dispatch.MailboxType mailboxType) { } public Akka.Actor.Internal.ChildRestartStats InitChild(Akka.Actor.IInternalActorRef actor) { } @@ -127,7 +126,6 @@ namespace Akka.Actor public void TerminatedQueuedFor(Akka.Actor.IActorRef subject, Akka.Util.Option customMessage) { } public bool TryGetChildStatsByName(string name, out Akka.Actor.Internal.IChildStats child) { } protected bool TryGetChildStatsByRef(Akka.Actor.IActorRef actor, out Akka.Actor.Internal.ChildRestartStats child) { } - public bool TryGetSingleChild(string name, out Akka.Actor.IInternalActorRef child) { } public void UnbecomeStacked() { } protected void UnreserveChild(string name) { } public Akka.Actor.IActorRef Unwatch(Akka.Actor.IActorRef subject) { } diff --git a/src/core/Akka/Actor/ActorCell.Children.cs b/src/core/Akka/Actor/ActorCell.Children.cs index cc08f7ca42b..163418c39db 100644 --- a/src/core/Akka/Actor/ActorCell.Children.cs +++ b/src/core/Akka/Actor/ActorCell.Children.cs @@ -353,35 +353,19 @@ protected bool TryGetChildStatsByRef(IActorRef actor, out ChildRestartStats chil /// /// N/A /// N/A - [Obsolete("Use TryGetSingleChild [0.7.1]")] public IInternalActorRef GetSingleChild(string name) - { - return TryGetSingleChild(name, out var child) ? child : ActorRefs.Nobody; - } - - - - /// - /// TBD - /// - /// TBD - /// TBD - /// TBD - public bool TryGetSingleChild(string name, out IInternalActorRef child) { if (name.IndexOf('#') < 0) { // optimization for the non-uid case if (ChildrenContainer.TryGetByName(name, out var stats) && stats is ChildRestartStats r) { - child = r.Child; - return true; + return r.Child; } if (TryGetFunctionRef(name, out var functionRef)) { - child = functionRef; - return true; + return functionRef; } } else @@ -389,20 +373,17 @@ public bool TryGetSingleChild(string name, out IInternalActorRef child) var (s, uid) = GetNameAndUid(name); if (TryGetChildRestartStatsByName(s, out var stats) && (uid == ActorCell.UndefinedUid || uid == stats.Uid)) { - child = stats.Child; - return true; + return stats.Child; } if (TryGetFunctionRef(s, uid, out var functionRef)) { - child = functionRef; - return true; + return functionRef; } } - child = ActorRefs.Nobody; - return false; + return ActorRefs.Nobody; } - + /// /// TBD /// diff --git a/src/core/Akka/Actor/ActorCell.cs b/src/core/Akka/Actor/ActorCell.cs index 1a3a32a7e3e..d8eabb6c149 100644 --- a/src/core/Akka/Actor/ActorCell.cs +++ b/src/core/Akka/Actor/ActorCell.cs @@ -236,7 +236,7 @@ public void Init(bool sendSupervise, MailboxType mailboxType) [Obsolete("Use TryGetChildStatsByName [0.7.1]", true)] public IInternalActorRef GetChildByName(string name) //TODO: Should return Option[ChildStats] { - return TryGetSingleChild(name, out var child) ? child : ActorRefs.Nobody; + return GetSingleChild(name); } IActorRef IActorContext.Child(string name) diff --git a/src/core/Akka/Actor/LocalActorRef.cs b/src/core/Akka/Actor/LocalActorRef.cs index 244bec0efbc..11e9f6cad9d 100644 --- a/src/core/Akka/Actor/LocalActorRef.cs +++ b/src/core/Akka/Actor/LocalActorRef.cs @@ -209,8 +209,7 @@ protected override void TellInternal(object message, IActorRef sender) /// public override IInternalActorRef GetSingleChild(string name) { - IInternalActorRef child; - return _cell.TryGetSingleChild(name, out child) ? child : ActorRefs.Nobody; + return _cell.GetSingleChild(name); } ///