optimize away boxing for messages

pull/5/head
Evan Hemsley 2019-12-06 12:01:56 -08:00
parent a29251f3d8
commit 4a2771d6a2
5 changed files with 182 additions and 91 deletions

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class MessageStore
{
private Dictionary<Type, TypedMessageStore> Stores = new Dictionary<Type, TypedMessageStore>(512);
private void RegisterMessageType<TMessage>() where TMessage : struct, IMessage
{
Stores.Add(typeof(TMessage), new TypedMessageStore<TMessage>());
}
private TypedMessageStore<TMessage> Lookup<TMessage>() where TMessage : struct, IMessage
{
if (!Stores.ContainsKey(typeof(TMessage))) { RegisterMessageType<TMessage>(); }
return Stores[typeof(TMessage)] as TypedMessageStore<TMessage>;
}
public void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{
Lookup<TMessage>().Add(message);
}
public void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
Lookup<TMessage>().Add(message, time);
}
public void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
Lookup<TMessage>().AddIgnoringTimeDilation(message, time);
}
public TMessage First<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().First();
}
public IEnumerable<TMessage> All<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().All();
}
public bool Any<TMessage>() where TMessage : struct, IMessage
{
return Lookup<TMessage>().Any();
}
public void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
foreach (var store in Stores.Values)
{
store.ProcessDelayedMessages(dilatedDelta, realtimeDelta);
}
}
public void ClearAll()
{
foreach (var store in Stores.Values)
{
store.Clear();
}
}
}
}

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
namespace Encompass
{
internal abstract class TypedMessageStore
{
public abstract void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta);
public abstract void Clear();
}
internal class TypedMessageStore<TMessage> : TypedMessageStore where TMessage : struct, IMessage
{
private readonly List<TMessage> store = new List<TMessage>(128);
private readonly List<(TMessage, double)> delayedStore = new List<(TMessage, double)>(128);
private readonly List<(TMessage, double)> delayedStoreIgnoringTimeDilation = new List<(TMessage, double)>(128);
public override void ProcessDelayedMessages(double dilatedDelta, double realtimeDelta)
{
for (int i = delayedStore.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStore[i];
var updatedTime = time - dilatedDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStore.RemoveAt(i);
}
else
{
delayedStore[i] = (message, updatedTime);
}
}
for (int i = delayedStoreIgnoringTimeDilation.Count - 1; i >= 0; i--)
{
var (message, time) = delayedStoreIgnoringTimeDilation[i];
var updatedTime = time - realtimeDelta;
if (updatedTime <= 0)
{
Add(message);
delayedStoreIgnoringTimeDilation.RemoveAt(i);
}
else
{
delayedStoreIgnoringTimeDilation[i] = (message, updatedTime);
}
}
}
public void Add(TMessage message)
{
store.Add(message);
}
public void Add(TMessage message, double time)
{
delayedStore.Add((message, time));
}
public void AddIgnoringTimeDilation(TMessage message, double time)
{
delayedStoreIgnoringTimeDilation.Add((message, time));
}
public TMessage First()
{
return store[0];
}
public bool Any()
{
return store.Count > 0;
}
public IEnumerable<TMessage> All()
{
return store;
}
public override void Clear()
{
store.Clear();
}
}
}

View File

@ -450,7 +450,7 @@ namespace Encompass
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayed(message, time);
messageManager.AddMessage(message, time);
}
/// <summary>
@ -459,20 +459,7 @@ namespace Encompass
/// <param name="time">The time in seconds that will elapse before the message is sent.</param>
protected void SendMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayedIgnoringTimeDilation(message, time);
}
// unparameterized version to enable dynamic dispatch
protected void SendMessage(IMessage message)
{
var type = message.GetType();
if (!sendTypes.Contains(type) || !type.IsValueType)
{
throw new IllegalSendException("Engine {0} tried to send undeclared Message {1}", GetType().Name, type.Name);
}
messageManager.AddMessage(message);
messageManager.AddMessageIgnoringTimeDilation(message, time);
}
internal void AddExistingComponent<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
@ -482,7 +469,7 @@ namespace Encompass
internal bool AddPendingComponent<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{
return componentUpdateManager.AddPendingComponent<TComponent>(entity, component, priority);
return componentUpdateManager.AddPendingComponent(entity, component, priority);
}
/// <summary>
@ -509,7 +496,7 @@ namespace Encompass
/// </exception>
protected TMessage ReadMessage<TMessage>() where TMessage : struct, IMessage
{
return ReadMessages<TMessage>().First();
return messageManager.First<TMessage>();
}
/// <summary>
@ -525,7 +512,7 @@ namespace Encompass
throw new IllegalReadException("Engine {0} tried to read undeclared Message {1}", GetType().Name, typeof(TMessage).Name);
}
return ReadMessages<TMessage>().Any();
return messageManager.Any<TMessage>();
}
/// <summary>

View File

@ -1,102 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal class MessageManager
{
private readonly TimeManager timeManager;
private readonly Dictionary<Type, List<IMessage>> messageTypeToMessages = new Dictionary<Type, List<IMessage>>();
private readonly List<(IMessage, double)> delayedMessages = new List<(IMessage, double)>();
private readonly List<(IMessage, double)> delayedMessagesIgnoringTimeDilation = new List<(IMessage, double)>();
private readonly MessageStore messageStore = new MessageStore();
public MessageManager(TimeManager timeManager)
{
this.timeManager = timeManager;
}
internal void RegisterMessageType(Type messageType)
internal void AddMessage<TMessage>(TMessage message) where TMessage : struct, IMessage
{
if (!messageTypeToMessages.ContainsKey(messageType))
{
messageTypeToMessages.Add(messageType, new List<IMessage>());
}
messageStore.AddMessage<TMessage>(message);
}
internal void AddMessage(IMessage message)
internal void AddMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
var type = message.GetType();
messageTypeToMessages[type].Add(message);
messageStore.AddMessage(message, time);
}
internal void AddMessageDelayed(IMessage message, double time)
internal void AddMessageIgnoringTimeDilation<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List<IMessage>()); }
delayedMessages.Add((message, time));
}
internal void AddMessageDelayedIgnoringTimeDilation(IMessage message, double time)
{
if (!messageTypeToMessages.ContainsKey(message.GetType())) { messageTypeToMessages.Add(message.GetType(), new List<IMessage>()); }
delayedMessagesIgnoringTimeDilation.Add((message, time));
messageStore.AddMessageIgnoringTimeDilation(message, time);
}
internal void ClearMessages()
{
foreach (var entry in messageTypeToMessages)
{
entry.Value.Clear();
}
messageStore.ClearAll();
}
internal void ProcessDelayedMessages(double dt)
{
for (int i = delayedMessages.Count - 1; i >= 0; i--)
{
var (message, time) = delayedMessages[i];
var updatedTime = time - (dt * timeManager.TimeDilationFactor);
if (updatedTime <= 0)
{
AddMessage(message);
delayedMessages.RemoveAt(i);
}
else
{
delayedMessages[i] = (message, updatedTime);
}
}
for (int i = delayedMessagesIgnoringTimeDilation.Count - 1; i >= 0; i--)
{
var (message, time) = delayedMessagesIgnoringTimeDilation[i];
var updatedTime = time - dt;
if (updatedTime <= 0)
{
AddMessage(message);
delayedMessagesIgnoringTimeDilation.RemoveAt(i);
}
else
{
delayedMessagesIgnoringTimeDilation[i] = (message, updatedTime);
}
}
messageStore.ProcessDelayedMessages(dt * timeManager.TimeDilationFactor, dt);
}
internal IEnumerable<TMessage> GetMessagesByType<TMessage>() where TMessage : struct, IMessage
{
return messageTypeToMessages.ContainsKey(typeof(TMessage)) ?
messageTypeToMessages[typeof(TMessage)].Cast<TMessage>() :
Enumerable.Empty<TMessage>();
return messageStore.All<TMessage>();
}
internal bool Any<TMessage>() where TMessage : struct, IMessage
{
return messageStore.Any<TMessage>();
}
internal TMessage First<TMessage>() where TMessage : struct, IMessage
{
return messageStore.First<TMessage>();
}
}
}

View File

@ -69,7 +69,7 @@ namespace Encompass
/// </summary>
public void SendMessage<TMessage>(TMessage message, double time) where TMessage : struct, IMessage
{
messageManager.AddMessageDelayed(message, time);
messageManager.AddMessage<TMessage>(message, time);
}
/// <summary>
@ -111,11 +111,6 @@ namespace Encompass
var messageReceiveTypes = engine.receiveTypes;
var messageSendTypes = engine.sendTypes;
foreach (var messageType in messageReceiveTypes.Union(messageSendTypes))
{
messageManager.RegisterMessageType(messageType);
}
foreach (var writePendingType in engine.writePendingTypes.Intersect(engine.readPendingTypes))
{
throw new EngineSelfCycleException("Engine {0} both writes and reads pending Component {1}", engine.GetType().Name, writePendingType.Name);