【发布时间】:2014-07-17 15:08:45
【问题描述】:
我有一个调用站点缓存,它由这个 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 这个私有变量不能被改变吗?
这是一个低级缓存,因此确定这是否是正确路径所花费的时间越少越好。
编辑: 这是针对 ryuJIT 和普通 JIT 尝试的 64 位 .net 4.5。
【问题讨论】:
-
请说明 CLR 版本、位数以及是否使用 RyuJIT :)
-
不,边界检查在 x86 和 x64 抖动中不会被消除。这将需要一个可以跨子表达式记住状态的优化器,这种优化会花费抖动无法承受的空间和时间。避免将其与缓存数组 Length 属性值的抖动混淆,这在 x64 抖动中很容易做到,因为它有足够的寄存器。
-
@HansPassant,通过检查输出反汇编,当我使用局部变量时,它仅与缓存长度进行一次比较,因此在此示例中总共进行了 4 次比较。当我依赖字段引用时,它会执行 6 次比较。诚然,我不是 amd64 组装方面的专家。
标签: c#