在这种情况下,JIT为什么不使用只读标志进行优化?
本文关键字:标志 优化 只读 这种情况下 JIT 为什么不 | 更新日期: 2023-09-27 18:13:32
我有一个由这个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]);
}
为什么要添加额外的绑定检查?只读标志不是告诉JIT这个私有变量不能被改变吗?
这是一个低级缓存,所以确定这是否是正确路径的时间越少越好。
编辑:
我在实时交易领域工作,并在试图从应用程序中挤出每一点性能的同时研究了这一点。
正如Hans在问题评论中所说,有一些优化是JITters选择忽略的。其中之一是注册readonly
成员变量,这些变量在一个方法中被多次读取。虽然从概念上讲,这似乎是一个简单的实现,认识到相同的成员变量被多次读取是很重要的。
简单地说,这是一种权衡,在绝大多数情况下,潜在的好处不值得为抖动付出代价。如果您的性能调优已经达到了微优化的地步,那么通过手动将成员变量提升到局部变量,显式地启用JITter来注册成员变量。这具有使JITter执行其他优化的连锁效应,例如消除边界检查。