在这种情况下,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这个私有变量不能被改变吗?

这是一个低级缓存,所以确定这是否是正确路径的时间越少越好。

编辑:

在这种情况下,JIT为什么不使用只读标志进行优化?

我在实时交易领域工作,并在试图从应用程序中挤出每一点性能的同时研究了这一点。

正如Hans在问题评论中所说,有一些优化是JITters选择忽略的。其中之一是注册readonly成员变量,这些变量在一个方法中被多次读取。虽然从概念上讲,这似乎是一个简单的实现,认识到相同的成员变量被多次读取是很重要的。

简单地说,这是一种权衡,在绝大多数情况下,潜在的好处不值得为抖动付出代价。如果您的性能调优已经达到了微优化的地步,那么通过手动将成员变量提升到局部变量,显式地启用JITter来注册成员变量。这具有使JITter执行其他优化的连锁效应,例如消除边界检查。