我有一个由此CachePage
类数组组成的调用站点缓存。缓存页面有一个readonly
标记数组,用于确定此页面是否正确使用。
internal class CachePage
{
internal CachePage(Token[] tokens)
{
this.tokens = tokens;
}
private readonly Token[] tokens;
// other members don't matter...
为了简化问题,在课堂上我有几个CheckTokens
方法,它们采用了不同数量的参数,WLOG都是这样的。
public bool CheckTokens(Token g0, Token g1, Token g2)
{
return (g2 == tokens[2] && g1 == tokens[1] && g0 == tokens[0]);
}
我向后遍历数组的元素,只产生一个绑定检查。或者我想。当我查看反汇编的输出时,我实际上看到每次比较它实际上都在执行边界检查。
但是,如果我像这样更改方法,则会消除额外的边界检查。
public bool CheckTokens(Token g0, Token g1, Token g2)
{
var t=tokens;
return (g2 == t[2] && g1 == t[1] && g0 == t[0]);
}
为什么要添加额外的绑定检查?没有readonly标志告诉JIT这个私有变量不能被变异吗?
这是一个低级缓存,所以花在确定这条路径是否越好的时间越少越好。
编辑: 这是针对rbitJIT和普通JIT尝试的64位.net 4.5。
答案 0 :(得分:1)
我在实时交易的世界里工作,并在尝试从应用程序中挤出最后一点性能时对此进行调查。
正如汉斯在问题评论中所说,JITters选择忽略了一些优化。其中之一是注册readonly
个成员变量,这些变量在方法中被多次读取。虽然从概念上讲这似乎是一个简单的实现,但是认识到多次读取相同的成员变量并非易事。
简单地说,这是一种权衡,在绝大多数情况下,潜在的好处并不值得JITter付出代价。如果您的性能调优已达到微优化的程度,那么明确启用JITter以通过手动将其提升为局部变量来注册成员变量。这可以使JITter执行其他优化,例如消除边界检查。