如何才能真正快速地做到这一点?
我有一个框架,允许用户查询特定的数据源(足球经理 2010 游戏内数据库,对于那些感兴趣的人来说)。
在此框架中,我的框架可以运行两种不同的模式:实时模式和缓存模式。我希望使用此框架的用户能够通过调用不同的构造函数(例如 new Context(Mode.Cached)
)来进行切换。这应该是用户应该进行的唯一切换,因此他仍然可以进行所有相同的 Linq 调用,但只需在他的应用程序更适合时使用缓存模式。清除。
我决定使用 PostSharp 应该是我的最佳选择,因为:
- 在每个属性上创建一个方面(已由属性装饰)
- 在该方面,检查我们是否处于缓存或实时状态 模式
- 从内存或缓存返回值,
这可行。但!速度不够好。对 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 毫秒非常合理。
但是!在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毫秒,超过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; }
我怎样才能让它表现良好?!
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 offset, 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?!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用 PostSharp 2.0 的 LocationInterceptionAspect 可以获得更好的结果。
但是你应该避免在运行时使用 eventArgs.Method.ReturnType ;而是获取方法 RuntimeInitialize 中的值并将其存储在字段中。所以运行时不使用System.Reflection。
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.
不要使用 new Context(Mode.Cached)) 创建上下文,而是使用一个创建上下文的工厂方法。然后在两个不同的类中实现这两种行为,这两个类共享抽象超类型所需的任何内容。使用方面和反射来解决没有简单直接解决方案的问题。
替换
为
其中
PlayerAttrs
有一个运算符 Int16,可以根据需要将自身转换为 Int16,具有所需的偏移量,并执行适当的缓存/非缓存查找。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
with
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.反思可能代价高昂。您可能尝试的一件事是在运行时实际编译此类的包装器,并节省当前的每次调用命中率。
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.