How to get this really fast?
I have a framework that allows users to do queries to a specific datasource (the Football Manager 2010 ingame database, for those of you interested).
In this framework, I have two different modes wherein my framework can run: realtime and cached mode. I want users who use this framework to be able to switch by just calling a different constructor (e.g. new Context(Mode.Cached)
). That should be the only switch a user should make, so he can still have all the same Linq calls, but just use Cached mode when his application fits better. Clear.
I had decided that using PostSharp should be my best choice because:
- Create an aspect on every property (that's already been decorated by an attribute)
- In that aspect, check whether we are in
Cached
orRealtime
mode - Return the value either from memory or from cache
Well that works. BUT! Speed is not good enough. When doing the following on 90.000 objects:
foreach (Player p in fm.Players)
{
int ca = (short)ProcessManager.ReadFromBuffer(p.OriginalBytes, PlayerOffsets.Ca, typeof(Int16));
}
It takes only 63 ms. (ReadFromBuffer is a highly optimized function which takes byte[], int, Type
and returns object
), 63 ms is very reasonable considering the large amounts of objects.
But! In PostSharp, I implemented quite the same using this:
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
if (eventArgs.Method.Name.StartsWith("~get_"))
{
if (Global.DatabaseMode == DatabaseModeEnum.Cached)
{
byte[] buffer = ((BaseObject)eventArgs.Instance).OriginalBytes;
eventArgs.ReturnValue =
ProcessManager.ReadFromBuffer(buffer, this.Offset, eventArgs.Method.ReturnType);
}
Now I call this using
foreach (Player p in fm.Players)
{
int ca = p.CA;
}
And it takes 782 ms, more than 10 times as much!
I created the aspect as:
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
internal class FMEntityAttribute : OnMethodInvocationAspect
{
public FMEntityAttribute(int off开发者_开发技巧set, int additionalStringOffset)
{
this.Offset = offset;
this.AdditionalStringOffset = additionalStringOffset;
}
//blah blah AOP code
}
And the property is decorated like
[FMEntityAttribute(PlayerOffsets.Ca)]
public Int16 CA { get; set; }
How can I get this to perform well?!
You could get much better results using PostSharp 2.0's LocationInterceptionAspect.
But then you should avoid using eventArgs.Method.ReturnType at runtime; rather get the value in method RuntimeInitialize and store it in a field. So System.Reflection is not used at runtime.
Instead of creating your context using new Context(Mode.Cached))
, have a factory method which creates a context. Then implement your two behaviours in two different classes which share whatever they need of an abstract super type. Use aspects and reflection to solve problems which don't have a simple direct solution.
replace
[FMEntityAttribute(PlayerOffsets.Ca)] public Int16 CA { get; }
with
public Int16 CA { get { return PlayerAttrs.Ca.Get(this); } }
where PlayerAttrs
has an operator Int16 to convert itself to Int16 on demand,has the offset required, and performs the appropriate cached/non-cached lookup.
- Use CompileTimeValidate method to check if its a property or not
Reflection can be expensive. One thing you might try is actually compiling a wrapper for this class at runtime, and saving yourself the per-call hit that you currently have.
精彩评论