From f3ff1b74d22b79c0eee651948cf46b55587a0107 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 31 May 2025 22:53:53 +0300 Subject: [PATCH] perf: reduced network manager memory allocations --- Shared/Network/NetworkManager.cs | 117 ++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 33 deletions(-) diff --git a/Shared/Network/NetworkManager.cs b/Shared/Network/NetworkManager.cs index 912de13..6175ed5 100644 --- a/Shared/Network/NetworkManager.cs +++ b/Shared/Network/NetworkManager.cs @@ -10,6 +10,12 @@ namespace Syntriax.Engine.Network; public class NetworkManager : UniverseObject, INetworkManager { private INetworkCommunicator networkCommunicator = null!; + + private readonly Dictionary>> clientListenerDelegates = []; + private readonly Dictionary>> serverListenerDelegates = []; + + private readonly Dictionary>> clientPacketListeners = []; + private readonly Dictionary>> serverPacketListeners = []; private readonly List<(Type packetType, Delegate callback)> delegates = []; public INetworkCommunicator NetworkCommunicator @@ -28,9 +34,6 @@ public class NetworkManager : UniverseObject, INetworkManager } } - private readonly Dictionary>> clientPacketListeners = []; - private readonly Dictionary>> serverPacketListeners = []; - private readonly Dictionary _networkEntities = []; public IReadOnlyDictionary NetworkEntities => _networkEntities; @@ -39,13 +42,41 @@ public class NetworkManager : UniverseObject, INetworkManager public NetworkManager() { - CacheDelegates(); + CachePacketDelegates(); + CacheListenerDelegates(); _networkEntityCollector.OnCollected.AddListener(OnCollected); _networkEntityCollector.OnRemoved.AddListener(OnRemoved); } - private void CacheDelegates() + private void CacheListenerDelegates() + { + foreach (Type clientListenerClass in GetClassesImplementing(typeof(IPacketListenerClient<>))) + { + Dictionary> clientInterfaceListeners = []; + clientListenerDelegates.Add(clientListenerClass, clientInterfaceListeners); + foreach (Type clientListenerInterface in GetInterfacesImplementing(clientListenerClass, typeof(IPacketListenerClient<>))) + { + Type clientListenerParameterType = clientListenerInterface.GetGenericArguments().First(); + List delegateDataList = clientListenerInterface.GetMethods().Where(m => m.Name == nameof(IPacketListenerClient.OnClientPacketArrived)).Select(m => new DelegateData(clientListenerParameterType, m)).ToList(); + clientInterfaceListeners.Add(clientListenerParameterType, delegateDataList); + } + } + + foreach (Type serverListenerClass in GetClassesImplementing(typeof(IPacketListenerServer<>))) + { + Dictionary> serverInterfaceListeners = []; + serverListenerDelegates.Add(serverListenerClass, serverInterfaceListeners); + foreach (Type serverListenerInterface in GetInterfacesImplementing(serverListenerClass, typeof(IPacketListenerServer<>))) + { + Type serverListenerParameterType = serverListenerInterface.GetGenericArguments().First(); + List delegateDataList = serverListenerInterface.GetMethods().Where(m => m.Name == nameof(IPacketListenerServer.OnServerPacketArrived)).Select(m => new DelegateData(serverListenerParameterType, m)).ToList(); + serverInterfaceListeners.Add(serverListenerParameterType, delegateDataList); + } + } + } + + private void CachePacketDelegates() { // Find network packets implementing INetworkPacket IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) @@ -91,6 +122,22 @@ public class NetworkManager : UniverseObject, INetworkManager } } + private static IEnumerable GetClassesImplementing(Type type) + => AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(a => + a.GetTypes().Where( + t => t.GetInterfaces().Any( + i => i.IsGenericType && i.GetGenericTypeDefinition() == type + ) + ) + ); + + private static IEnumerable GetInterfacesImplementing(Type type, Type interfaceType) + => type.GetInterfaces().Where( + i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType + ); + private void OnPacketReceived(string senderClientId, T entityDataPacket) { Type packetType = typeof(T); @@ -128,37 +175,35 @@ public class NetworkManager : UniverseObject, INetworkManager if (!_networkEntities.TryAdd(collectedBehaviour.Id, collectedBehaviour)) throw new($"Unable to add {collectedBehaviour.Id} to {nameof(NetworkManager)}"); - foreach (Type clientListenerType in collectedBehaviour.GetType().GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketListenerClient<>))) - { - Type clientListenerParameterType = clientListenerType.GetGenericArguments().First(); - MethodInfo clientListenerReceiveMethod = clientListenerType.GetMethods().First(m => m.Name == nameof(IPacketListenerClient.OnClientPacketArrived)); + if (clientListenerDelegates.TryGetValue(collectedBehaviour.GetType(), out Dictionary>? clientInterfaceDelegates)) + foreach ((Type clientListenerInterfaceType, List clientDelegateDataList) in clientInterfaceDelegates) + foreach ((Type parameterType, MethodInfo receiveMethod) in clientDelegateDataList) + { + if (!clientPacketListeners.TryGetValue(parameterType, out Dictionary>? clientListeners)) + { + clientListeners = []; + clientPacketListeners.Add(parameterType, clientListeners); + } - if (!clientPacketListeners.TryGetValue(clientListenerParameterType, out Dictionary>? clientListeners)) - { - clientListeners = []; - clientPacketListeners.Add(clientListenerParameterType, clientListeners); - } + Event clientListenerEvent = new(); + clientListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(collectedBehaviour, [@object])); + clientListeners.Add(collectedBehaviour.Id, clientListenerEvent); + } - Event clientListenerEvent = new(); - clientListenerEvent.AddListener((sender, @object) => clientListenerReceiveMethod.Invoke(collectedBehaviour, [@object])); - clientListeners.Add(collectedBehaviour.Id, clientListenerEvent); - } + if (serverListenerDelegates.TryGetValue(collectedBehaviour.GetType(), out Dictionary>? serverInterfaceDelegates)) + foreach ((Type serverListenerInterfaceType, List serverDelegateDataList) in serverInterfaceDelegates) + foreach ((Type parameterType, MethodInfo receiveMethod) in serverDelegateDataList) + { + if (!serverPacketListeners.TryGetValue(parameterType, out Dictionary>? serverListeners)) + { + serverListeners = []; + serverPacketListeners.Add(parameterType, serverListeners); + } - foreach (Type serverListenerType in collectedBehaviour.GetType().GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketListenerServer<>))) - { - Type serverListenerParameterType = serverListenerType.GetGenericArguments().First(); - MethodInfo serverListenerReceiveMethod = serverListenerType.GetMethods().First(m => m.Name == nameof(IPacketListenerServer.OnServerPacketArrived)); - - if (!serverPacketListeners.TryGetValue(serverListenerParameterType, out Dictionary>? serverListeners)) - { - serverListeners = []; - serverPacketListeners.Add(serverListenerParameterType, serverListeners); - } - - Event serverListenerEvent = new(); - serverListenerEvent.AddListener((sender, @object) => serverListenerReceiveMethod.Invoke(collectedBehaviour, [sender, @object])); - serverListeners.Add(collectedBehaviour.Id, serverListenerEvent); - } + Event serverListenerEvent = new(); + serverListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(collectedBehaviour, [sender, @object])); + serverListeners.Add(collectedBehaviour.Id, serverListenerEvent); + } } private void OnRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) @@ -172,4 +217,10 @@ public class NetworkManager : UniverseObject, INetworkManager _networkEntityCollector.Assign(universe); NetworkCommunicator = this.GetRequiredUniverseObjectInParent(); } + + private record struct DelegateData(Type ParameterType, MethodInfo ReceiveMethod) + { + public static implicit operator (Type ParameterType, MethodInfo ReceiveMethod)(DelegateData value) => (value.ParameterType, value.ReceiveMethod); + public static implicit operator DelegateData((Type ParameterType, MethodInfo ReceiveMethod) value) => new(value.ParameterType, value.ReceiveMethod); + } }