perf: network manager registration memory allocation reduced further by removing boxing on some methods

This commit is contained in:
Syntriax 2025-06-01 11:19:33 +03:00
parent 4824496f1a
commit e27b825990
2 changed files with 68 additions and 25 deletions

2
Engine

@ -1 +1 @@
Subproject commit 86c9ed2ba98e05f21a952b58c49af5954207e006
Subproject commit 7a3202a053b6fd7c9adf9bc210bf8026f3412328

View File

@ -15,11 +15,14 @@ public class NetworkManager : UniverseObject, INetworkManager
private readonly Dictionary<Type, Dictionary<Type, List<MethodInfo>>> clientPacketArrivalMethods = [];
private readonly Dictionary<Type, Dictionary<Type, List<MethodInfo>>> serverPacketArrivalMethods = [];
private readonly Dictionary<Type, Dictionary<string, Event<string, object>>> clientPacketRouters = [];
private readonly Dictionary<Type, Dictionary<string, Event<string, object>>> serverPacketRouters = [];
private readonly Dictionary<Type, Dictionary<string, object>> clientPacketRouters = [];
private readonly Dictionary<Type, Dictionary<string, object>> serverPacketRouters = [];
private readonly List<(Type packetType, Delegate callback)> packetRetrievalDelegates = [];
private readonly Dictionary<Type, MethodInfo> clearRoutesMethods = [];
private readonly Dictionary<Type, MethodInfo> registerPacketListenersMethods = [];
private readonly Dictionary<string, INetworkEntity> _networkEntities = [];
public IReadOnlyDictionary<string, INetworkEntity> NetworkEntities => _networkEntities;
@ -96,37 +99,41 @@ public class NetworkManager : UniverseObject, INetworkManager
private static void BroadcastPacket<T>(
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters,
Dictionary<Type, Dictionary<string, object>> packetRouters,
string senderClientId,
T entityDataPacket)
{
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, Event<string, object>>? routers))
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, object>? routers))
return;
foreach ((string id, Event<string, object> routerEvent) in routers)
foreach ((string id, object routerEventReference) in routers)
{
Event<string, T> routerEvent = (Event<string, T>)routerEventReference;
routerEvent.Invoke(senderClientId, entityDataPacket!);
}
}
private static void RoutePacket<T>(
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters,
Dictionary<Type, Dictionary<string, object>> packetRouters,
string entityId,
string senderClientId,
T entityDataPacket)
{
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, Event<string, object>>? routers))
if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary<string, object>? routers))
return;
if (!routers.TryGetValue(entityId, out Event<string, object>? routerEvent))
if (!routers.TryGetValue(entityId, out object? routerEventReference))
return;
Event<string, T> routerEvent = (Event<string, T>)routerEventReference;
routerEvent.Invoke(senderClientId, entityDataPacket!);
}
#endregion
#region Packet Routers
private static void RegisterPacketRoutersFor(
private void RegisterPacketRoutersFor(
INetworkEntity behaviour,
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters,
Dictionary<Type, Dictionary<string, object>> packetRouters,
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods,
NetworkType networkType)
{
@ -136,34 +143,44 @@ public class NetworkManager : UniverseObject, INetworkManager
foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods)
foreach (MethodInfo receiveMethod in methods)
{
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, Event<string, object>>? routers))
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, object>? routers))
{
routers = [];
packetRouters.Add(packetType, routers);
}
Event<string, object> packetListenerEvent = new();
RegisterPacketListenerEvent(behaviour, packetListenerEvent, receiveMethod, networkType);
object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType);
routers.Add(behaviour.Id, packetListenerEvent);
}
}
private static void RegisterPacketListenerEvent(
private object CreateEventAndRegister(Type packetType, INetworkEntity behaviour, NetworkType networkType)
{
Type genericEventType = typeof(Event<,>).MakeGenericType(typeof(string), packetType);
object packetListenerEvent = Activator.CreateInstance(genericEventType)!;
if (!registerPacketListenersMethods.TryGetValue(packetType, out MethodInfo? registerPacketListenerMethod))
throw new($"{nameof(RegisterPacketListenerEvent)} for {packetType.Name} has not been cached.");
registerPacketListenerMethod.Invoke(this, [behaviour, packetListenerEvent, networkType]);
return packetListenerEvent;
}
private static void RegisterPacketListenerEvent<T>(
INetworkEntity behaviour,
Event<string, object> packetListenerEvent,
MethodInfo receiveMethod,
Event<string, T> packetListenerEvent,
NetworkType networkType)
{
switch (networkType)
{
case NetworkType.Client: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [@object])); break;
case NetworkType.Server: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [sender, @object])); break;
case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClient<T>)behaviour).OnClientPacketArrived(packet)); break;
case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServer<T>)behaviour).OnServerPacketArrived(sender, packet)); break;
}
}
private static void UnregisterPacketRoutersFor(
private void UnregisterPacketRoutersFor(
INetworkEntity behaviour,
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters,
Dictionary<Type, Dictionary<string, object>> packetRouters,
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods)
{
if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary<Type, List<MethodInfo>>? arrivalMethods))
@ -171,16 +188,25 @@ public class NetworkManager : UniverseObject, INetworkManager
foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods)
{
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, Event<string, object>>? routers))
if (!packetRouters.TryGetValue(packetType, out Dictionary<string, object>? routers))
continue;
if (!routers.TryGetValue(behaviour.Id, out Event<string, object>? routerEvent))
if (!routers.TryGetValue(behaviour.Id, out object? routerEventReference))
continue;
routers.Remove(behaviour.Id);
routerEvent.Clear();
if (!clearRoutesMethods.TryGetValue(packetType, out MethodInfo? clearRouterMethod))
continue;
clearRouterMethod.Invoke(this, [routerEventReference]);
}
}
private static void ClearRouter<T>(object routerEventReference)
{
Event<string, T> routerEvent = (Event<string, T>)routerEventReference;
routerEvent.Clear();
}
#endregion
#region Engine Callbacks
@ -217,6 +243,7 @@ public class NetworkManager : UniverseObject, INetworkManager
public NetworkManager()
{
CachePacketRetrievalDelegates();
CacheRegistrationMethods();
CachePacketArrivalMethods();
_networkEntityCollector.OnCollected.AddListener(OnCollected);
@ -242,6 +269,22 @@ public class NetworkManager : UniverseObject, INetworkManager
}
}
private void CacheRegistrationMethods()
{
CacheRegistrationMethods(registerPacketListenersMethods, nameof(RegisterPacketListenerEvent));
CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter));
}
private void CacheRegistrationMethods(Dictionary<Type, MethodInfo> registrationMethods, string methodName)
{
MethodInfo registerPacketMethod = typeof(NetworkManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static)!;
foreach ((Type packetType, Delegate callback) in packetRetrievalDelegates)
{
MethodInfo genericMethod = registerPacketMethod.MakeGenericMethod(packetType);
registrationMethods.Add(packetType, genericMethod);
}
}
private void CachePacketArrivalMethods()
{
CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<INetworkEntity>.OnClientPacketArrived));