.NET JIT优化可以内联这种方法

问题描述 投票:1回答:2

我知道静态方法可以通过.Net(和Mono)中的JIT优化来内联

我的问题是,是否可以实现访问其自身状态的实例方法?

例如:

public class CaseSensitiveLiteralStringMatcher : IStringMatcher
{
    private readonly LiteralToken _token;

    public CaseSensitiveLiteralStringMatcher(LiteralToken token)
    {
        _token = token;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsMatch(char containsChar, int position)
    {
        return containsChar == _token.Value[position];       
    }

}

上面的方法调用是否内联,即使它不是静态的并访问一些私有成员?

c# .net optimization jit
2个回答
2
投票

我在这里找到了很好的解读:http://blogs.microsoft.co.il/sasha/2007/02/27/jit-optimizations-inlining-and-interface-method-dispatching-part-1-of-n/

我的结论是,实例方法可以内联,但虚方法不能,因为调用的实际方法可以在运行时更改,并且无法使用源代码的静态分析来建立。

出于这个原因,我在我的问题中显示的方法可以内联,如果它不是接口方法 - 因为这意味着它必须在运行时通过vtable查找调度它是虚拟的。

说,有JIT优化技术可以针对“常见”情况优化虚拟方法内联,但是当内联方法与运行时所需的方法调用不匹配时,这些技术会带来回退,这意味着某些代码路径可能会受益更多来自内联其他人。


1
投票

好的。我有结果。答案似乎是JIT可以内联一个实现接口并访问或修改类成员的方法。

我的结果是:

  • 10 ^ 7次运行1:84毫秒
  • 10 ^ 7次运行2(通过接口):83 ms
  • 没有类或方法调用的10 ^ 7个内联循环运行:83毫秒

即具有和不具有界面的相同性能。此外,如果没有编译器积极的内联指令,性能仍然相同。

测试代码:

class Program
{
    internal interface IFastProcessor
    {
        void Process(int i);
    }

    internal sealed class FastProcessorImpl : IFastProcessor
    {
        private int number;

        public FastProcessorImpl(int number)
        {
            this.number = number;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Process(int i)
        {
            number = ((number + i) / (number + i)) * number;
        }
    }

    internal sealed class FastProcessor
    {
        private int number;

        public FastProcessor(int number)
        {
            this.number = number;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Process(int i)
        {
            number = ((number + i) / (number + i)) * number;
        }
    }

    static void Main(string[] args)
    {
        var sw1 = new Stopwatch();
        var processor1 = new FastProcessor(10);
        sw1.Start();
        for (int i = 1; i < 10000000; i++)
        {
            processor1.Process(i);
        }
        sw1.Stop();

        var sw2 = new Stopwatch();
        var processor2 = (IFastProcessor)new FastProcessorImpl(10);
        sw2.Start();
        for (int i = 1; i < 10000000; i++)
        {
            processor2.Process(i);
        }
        sw2.Stop();

        var number = 10;
        var sw3 = new Stopwatch();
        sw3.Start();
        for (int i = 1; i < 10000000; i++)
        {
            number = ((number + i) / (number + i)) * number;
        }
        sw3.Stop();

        Console.WriteLine($"Class: {sw1.ElapsedMilliseconds}ms, Interface: {sw2.ElapsedMilliseconds}ms, Inline: {sw3.ElapsedMilliseconds}ms");
    }
}

更新:我还尝试了一个带有虚方法的基类。令我极为惊讶的是,这也与内联版本完全相同,这意味着编译器可能正在优化虚拟调用,无论如何都允许JIT内联。所以我无法确定接口与虚拟方法的问题。但是,另一方面,可以肯定地说,在OPs问题中,我没有看到为什么不会内联该方法的原因。

© www.soinside.com 2019 - 2024. All rights reserved.