CLR会优化并内联这个GetHashCode()吗?

3
假设我们有这样一个值类型,其中字段是 readonly 并在构造过程中初始化:
public struct SomeValue
{
    private readonly Int32 field1;
    private readonly Int32 field2;

    ...
}

此外,假设我们有一个帮助类,可以让我们以可重用的方式实现复合类型的GetHashCode()方法:
public struct SomeValue
{
    ...

    public override Int32 GetHashCode()
    {
        return HashHelpers.GetHashCode(this.field1, this.field2);
    }
}

现在,编译器必须意识到在类型构造后,由于它们是readonly,字段值永远不会改变。因此,在SomeValue.GetHashCode()被JIT时,调用HashHelpers.GetHashCode()是否有可能被内联呢?


1
你所说的不是内联,而是短路 GetHashCode 实现。 - sisve
1
@Simon:在这个上下文中,你所说的“短路”是什么意思?请详细说明。 - Johann Gerell
你想让SomeValue.GetHashCode直接返回预先计算(在上次调用期间计算)的值。(通过SomeValue.GetHashCode创建更快的路径,短路。)内联意味着HashHelpers.GetHashCode代码(假设满足某些条件)将被插入到你的SomeValue.GetHashCode中以加速执行(通过避免需要堆栈修改、执行跳转等方法调用)。我只是说你所描述的不是内联。 - sisve
1
听起来你想要(或者至少询问)CLR是否具有某种能力来确定方法调用是否是确定性的(相同的输入=>相同的输出),并通过使用先前执行的缓存值替换它们来短路这些调用。 - sisve
@Simon:啊,我明白你的意思了。我猜想如果将其推到极致,那可能是我所指的,但实际上,我确实是指内联,也就是在没有函数调用开销的情况下执行完全相同的计算序列。 - Johann Gerell
显示剩余2条评论
4个回答

3

有一些关于内联的著名规则(.NET 4可能添加了更多规则)。如果HashHelpers.GetHashCode足够简单,它就会被内联。我认为在这种情况下那些只读字段没有任何意义。


3

你没有发布HashHelper方法的代码,但由于它应该很小且快速,所以很可能会被内联。

而且,JIT优化器完全能够在编译时评估表达式并用简单的常量值替换代码。但是,当您使用readonly成员时,这种情况不会发生。因为它的值是由构造函数确定的。优化器不考虑其他方法中的代码来猜测字段是否具有已知值。它必须能够在编译GetHashCode时检测到该值。

如果您使用const初始化readonly字段,并在GetHashCode实现中使用相同的const,则可以获得此结果。这非常丑陋。鉴于这种微小优化带来的非常有限的好处,这可能不是您应该考虑的事情。可能的胜利最多只有一纳秒左右。但很可能为零,因为优化器会将xor替换为mov。


代码:https://dev59.com/EnVC5IYBdhLWcg3wihqv#263416,经过更改以使其适用于多个参数。 - Johann Gerell
好的,我相信你的代码不会完全像那样。当JIT优化器进行内联决策时,细节很重要。然而,这并不改变我的答案。 - Hans Passant
好的,我确定我明确指出了它并不完全像这样,需要进行更改以使其适用于多个参数:http://pastebin.com/Fx6HCnPL - Johann Gerell

1
据我所知,你的字段声明为readonly与Jitter是否决定内联方法无关。
这里有一篇文章讨论了.NET 3.5sp1 jit使用的一些启发式方法。这在.NET 4中可能已经改变,并且可能在将来的版本中再次更改。

0
据我所知,对HashHelpers.GetHashCode()的调用不会被内联,它只会被视为CIL中该方法的普通调用。我可能错了,但我相当确定我没有错。

我认为他所说的是在JIT时间内进行内联,而不是编译时间。据我所知,C++.net编译器正在优化其CIL输出,但C#编译器只是将C#代码的翻译作为CIL输出,而没有进行任何优化。 - Julien Roncaglia
了解。如果我误解了问题,很抱歉。 - Liggi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接