开发者

How to get this really fast?

开发者 https://www.devze.com 2022-12-11 13:34 出处:网络
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).

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 or Realtime 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.


  1. 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号