我有一个框架,允许用户对特定数据源进行查询(对于那些感兴趣的人,可以使用Football Manager 2010游戏数据库)。
在这个框架中,我有两种不同的模式,我的框架可以运行:实时和缓存模式。我希望使用此框架的用户只需调用不同的构造函数即可切换(例如new Context(Mode.Cached)
)。这应该是用户应该进行的唯一切换,因此他仍然可以拥有所有相同的Linq调用,但只需在他的应用程序更适合时使用缓存模式。清除。
我已经决定使用PostSharp应该是我的最佳选择,因为:
Cached
或Realtime
模式那很有效。但!速度不够好。在90.000对象上执行以下操作时:
foreach (Player p in fm.Players)
{
int ca = (short)ProcessManager.ReadFromBuffer(p.OriginalBytes, PlayerOffsets.Ca, typeof(Int16));
}
只需63毫秒。 (ReadFromBuffer是一个高度优化的函数,需要byte[], int, Type
并返回object
),考虑到大量的对象,63 ms是非常合理的。
但是!在PostSharp中,我用这个实现了完全相同:
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);
}
现在我用
来调用它foreach (Player p in fm.Players)
{
int ca = p.CA;
}
它需要 782 ms ,超过10倍!
我创建了方面:
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
internal class FMEntityAttribute : OnMethodInvocationAspect
{
public FMEntityAttribute(int offset, int additionalStringOffset)
{
this.Offset = offset;
this.AdditionalStringOffset = additionalStringOffset;
}
//blah blah AOP code
}
该物业的装饰如
[FMEntityAttribute(PlayerOffsets.Ca)]
public Int16 CA { get; set; }
我怎样才能让它表现良好?!
答案 0 :(得分:2)
使用PostSharp 2.0的LocationInterceptionAspect可以获得更好的结果。
但是你应该避免在运行时使用eventArgs.Method.ReturnType;而是获取方法RuntimeInitialize中的值并将其存储在字段中。因此,在运行时不使用System.Reflection。
答案 1 :(得分:1)
答案 2 :(得分:1)
使用工厂方法创建上下文,而不是使用new Context(Mode.Cached))
创建上下文。然后在两个不同的类中实现您的两个行为,这些类共享他们需要的抽象超类型。使用方面和反射来解决没有简单直接解决方案的问题。
替换
[FMEntityAttribute(PlayerOffsets.Ca)] public Int16 CA { get; }
与
public Int16 CA { get { return PlayerAttrs.Ca.Get(this); } }
其中PlayerAttrs
有一个运算符Int16,可以根据需要将自身转换为Int16,具有所需的偏移量,并执行适当的缓存/非缓存查找。
答案 3 :(得分:0)
反思可能很昂贵。您可能尝试的一件事实际上是在运行时为此类编译包装器,并保存您当前拥有的每次调用命中。