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>>> clientPacketArrivalMethods = [];
private readonly Dictionary<Type, Dictionary<Type, List<MethodInfo>>> serverPacketArrivalMethods = []; 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, object>> clientPacketRouters = [];
private readonly Dictionary<Type, Dictionary<string, Event<string, object>>> serverPacketRouters = []; private readonly Dictionary<Type, Dictionary<string, object>> serverPacketRouters = [];
private readonly List<(Type packetType, Delegate callback)> packetRetrievalDelegates = []; 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 = []; private readonly Dictionary<string, INetworkEntity> _networkEntities = [];
public IReadOnlyDictionary<string, INetworkEntity> NetworkEntities => _networkEntities; public IReadOnlyDictionary<string, INetworkEntity> NetworkEntities => _networkEntities;
@ -96,37 +99,41 @@ public class NetworkManager : UniverseObject, INetworkManager
private static void BroadcastPacket<T>( private static void BroadcastPacket<T>(
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters, Dictionary<Type, Dictionary<string, object>> packetRouters,
string senderClientId, string senderClientId,
T entityDataPacket) 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; 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!); routerEvent.Invoke(senderClientId, entityDataPacket!);
}
} }
private static void RoutePacket<T>( private static void RoutePacket<T>(
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters, Dictionary<Type, Dictionary<string, object>> packetRouters,
string entityId, string entityId,
string senderClientId, string senderClientId,
T entityDataPacket) 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; return;
if (!routers.TryGetValue(entityId, out Event<string, object>? routerEvent)) if (!routers.TryGetValue(entityId, out object? routerEventReference))
return; return;
Event<string, T> routerEvent = (Event<string, T>)routerEventReference;
routerEvent.Invoke(senderClientId, entityDataPacket!); routerEvent.Invoke(senderClientId, entityDataPacket!);
} }
#endregion #endregion
#region Packet Routers #region Packet Routers
private static void RegisterPacketRoutersFor( private void RegisterPacketRoutersFor(
INetworkEntity behaviour, INetworkEntity behaviour,
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters, Dictionary<Type, Dictionary<string, object>> packetRouters,
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods, Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods,
NetworkType networkType) NetworkType networkType)
{ {
@ -136,34 +143,44 @@ public class NetworkManager : UniverseObject, INetworkManager
foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods) foreach ((Type packetType, List<MethodInfo> methods) in arrivalMethods)
foreach (MethodInfo receiveMethod in methods) 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 = []; routers = [];
packetRouters.Add(packetType, routers); packetRouters.Add(packetType, routers);
} }
Event<string, object> packetListenerEvent = new(); object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType);
RegisterPacketListenerEvent(behaviour, packetListenerEvent, receiveMethod, networkType);
routers.Add(behaviour.Id, packetListenerEvent); 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, INetworkEntity behaviour,
Event<string, object> packetListenerEvent, Event<string, T> packetListenerEvent,
MethodInfo receiveMethod,
NetworkType networkType) NetworkType networkType)
{ {
switch (networkType) switch (networkType)
{ {
case NetworkType.Client: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [@object])); break; case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClient<T>)behaviour).OnClientPacketArrived(packet)); break;
case NetworkType.Server: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [sender, @object])); break; case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServer<T>)behaviour).OnServerPacketArrived(sender, packet)); break;
} }
} }
private static void UnregisterPacketRoutersFor( private void UnregisterPacketRoutersFor(
INetworkEntity behaviour, INetworkEntity behaviour,
Dictionary<Type, Dictionary<string, Event<string, object>>> packetRouters, Dictionary<Type, Dictionary<string, object>> packetRouters,
Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods) Dictionary<Type, Dictionary<Type, List<MethodInfo>>> packetArrivalMethods)
{ {
if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary<Type, List<MethodInfo>>? arrivalMethods)) 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) 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; continue;
if (!routers.TryGetValue(behaviour.Id, out Event<string, object>? routerEvent)) if (!routers.TryGetValue(behaviour.Id, out object? routerEventReference))
continue; continue;
routers.Remove(behaviour.Id); if (!clearRoutesMethods.TryGetValue(packetType, out MethodInfo? clearRouterMethod))
routerEvent.Clear(); continue;
clearRouterMethod.Invoke(this, [routerEventReference]);
} }
} }
private static void ClearRouter<T>(object routerEventReference)
{
Event<string, T> routerEvent = (Event<string, T>)routerEventReference;
routerEvent.Clear();
}
#endregion #endregion
#region Engine Callbacks #region Engine Callbacks
@ -217,6 +243,7 @@ public class NetworkManager : UniverseObject, INetworkManager
public NetworkManager() public NetworkManager()
{ {
CachePacketRetrievalDelegates(); CachePacketRetrievalDelegates();
CacheRegistrationMethods();
CachePacketArrivalMethods(); CachePacketArrivalMethods();
_networkEntityCollector.OnCollected.AddListener(OnCollected); _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() private void CachePacketArrivalMethods()
{ {
CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<INetworkEntity>.OnClientPacketArrived)); CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<INetworkEntity>.OnClientPacketArrived));