C# 字段与属性。性能优化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9842917/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 10:51:41  来源:igfitidea点击:

Field vs Property. Optimisation of performance

c#.netperformancec#-4.0optimization

提问by Boppity Bop

Please note this question related to performance only. Lets skip design guidelines, philosophy, compatibility, portability and anything what is not related to pure performance. Thank you.

请注意此问题仅与性能有关。让我们跳过设计指南、哲学、兼容性、可移植性以及任何与纯性能无关的东西。谢谢你。

Now to the question. I always assumed that because C# getters/setters are really methods in disguise then reading public field must be faster than calling a getter.

现在来回答这个问题。我一直认为,因为 C# getter/setter 确实是伪装的方法,所以读取公共字段必须比调用 getter 更快。

So to make sure I did a test (the code below). However this test only produces expected results (ie fields are faster than getters at 34%) ifyou run it from inside Visual Studio.

所以为了确保我做了一个测试(下面的代码)。但是,如果您从 Visual Studio 内部运行此测试,则只会产生预期结果(即字段比 getter 快 34%)。

Once you run it from command line it shows pretty much the same timing...

一旦你从命令行运行它,它就会显示几乎相同的时间......

The only explanation could be is that the CLR does additional optimisation (correct me if I am wrong here).

唯一的解释可能是 CLR 做了额外的优化(如果我错了,请纠正我)。

I do not believe that in real application where those properties being used in much more sophisticated way they will be optimised in the same way.

我不相信在实际应用中,这些属性以更复杂的方式使用,它们会以相同的方式进行优化。

Please help me to prove or disprove the idea that in real life properties are slower than fields.

请帮助我证明或反驳现实生活中属性比字段慢的想法。

The question is - how should I modify the test classes to make the CLR change behaviour so the public field outperfroms the getters. OR show me that any property without internal logic will perform the same as a field (at least on the getter)

问题是 - 我应该如何修改测试类以使 CLR 更改行为,以便公共字段优于 getter。或者告诉我任何没有内部逻辑的属性都将与字段执行相同的操作(至少在 getter 上)

EDIT: I am only talking about Release x64 build.

编辑:我只是在谈论发布 x64 版本。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PropertyVsField
{
    class Program
    {
        static int LEN = 20000000;
        static void Main(string[] args)
        {
            List<A> a = new List<A>(LEN);
            List<B> b = new List<B>(LEN);

            Random r = new Random(DateTime.Now.Millisecond);

            for (int i = 0; i < LEN; i++)
            {
                double p = r.NextDouble();
                a.Add(new A() { P = p });
                b.Add(new B() { P = p });
            }

            Stopwatch sw = new Stopwatch();

            double d = 0.0;

            sw.Restart();
            for (int i = 0; i < LEN; i++)
            {
                d += a[i].P;
            }

            sw.Stop();

            Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d);

            sw.Restart();
            for (int i = 0; i < LEN; i++)
            {
                d += b[i].P;
            }

            sw.Stop();

            Console.WriteLine("      field. {0}. {1}.", sw.ElapsedTicks, d);

            Console.ReadLine();
        }
    }

    class A
    {
        public double P { get; set; }
    }
    class B
    {
        public double P;
    }
}

采纳答案by Heinzi

As others have already mentioned, the getters are inlined.

正如其他人已经提到的,吸气剂是内联的

If you want to avoid inlining, you have to

如果你想避免内联,你必须

  • replace the automatic properties with manual ones:

    class A 
    {
        private double p;
        public double P
        {
            get { return p; }
            set { p = value; }
        }
    } 
    
  • and tell the compiler not to inline the getter (or both, if you feel like it):

            [MethodImpl(MethodImplOptions.NoInlining)]
            get { return p; }
    
  • 用手动属性替换自动属性:

    class A 
    {
        private double p;
        public double P
        {
            get { return p; }
            set { p = value; }
        }
    } 
    
  • 并告诉编译器不要内联 getter(或者两者,如果你愿意的话):

            [MethodImpl(MethodImplOptions.NoInlining)]
            get { return p; }
    

Note that the first change does not make a difference in performance, whereas the second change shows a clear method call overhead:

请注意,第一个更改不会对性能产生影响,而第二个更改显示了明显的方法调用开销:

Manual properties:

手动属性:

auto getter. 519005. 10000971,0237547.
      field. 514235. 20001942,0475098.

No inlining of the getter:

没有内联吸气剂:

auto getter. 785997. 10000476,0385552.
      field. 531552. 20000952,077111.

回答by TomTom

The only explanation could be is that the CLR does additional optimisation (correrct me if I am wrong here).

唯一的解释可能是 CLR 做了额外的优化(如果我错了,请纠正我)。

Yes, it is called inlining. It is done in the compiler (machine code level - i.e. JIT). As the getter/setter are trivial (i.e. very simple code) the method calls are destroyed and the getter/setter written in the surrounding code.

是的,它被称为内联。它是在编译器中完成的(机器代码级别 - 即 JIT)。由于 getter/setter 是微不足道的(即非常简单的代码),因此方法调用被销毁,并且 getter/setter 写入周围的代码。

This does not happen in debug mode in order to support debugging (i.e. the ability to set a breakpoint in a getter or setter).

这不会在调试模式下发生以支持调试(即在 getter 或 setter 中设置断点的能力)。

In visual studio there is no way to do that in the debugger. Compile release, run without attached debugger and you will get the full optimization.

在 Visual Studio 中,调试器无法做到这一点。编译发布,在没有附加调试器的情况下运行,您将获得完整的优化。

I do not believe that in real application where those properties being used in much more sophisticated way they will be optimised in the same way.

我不相信在实际应用中,这些属性以更复杂的方式使用,它们会以相同的方式进行优化。

The world is full of illusions that are wrong. They will be optimized as they are still trivial (i.e. simple code, so they are inlined).

这个世界充满了错误的幻想。它们将被优化,因为它们仍然是微不足道的(即简单的代码,所以它们是内联的)。

回答by Guvante

The JIT will inline any method (not just a getter) that its internal metrics determine will be faster inlined. Given that a standard property is return _Property;it will be inlined in every case.

JIT 将内联其内部指标确定将更快内联的任何方法(不仅仅是 getter)。鉴于标准属性 isreturn _Property;它将在每种情况下内联。

The reason you are seeing different behavior is that in Debug mode with a debugger attached, the JIT is significantly handicapped, to ensure that any stack locations match what you would expect from the code.

您看到不同行为的原因是,在附加了调试器的 Debug 模式下,JIT 存在明显障碍,无法确保任何堆栈位置与您对代码的期望相匹配。

You are also forgetting the number one rule of performance, testing beats thinking. For instance even though quick sort is asymptotically faster than insertion sort, insertion sort is actually faster for extremely small inputs.

你也忘记了性能的第一条规则,测试胜过思考。例如,尽管快速排序比插入排序渐进快,但对于极小的输入,插入排序实际上更快。

回答by JamieSee

Have a look at the Properties vs Fields – Why Does it Matter? (Jonathan Aneja)blog article from one of the VB team members on MSDN. He outlines the property versus fields argument and also explains trivial properties as follows:

看看属性与字段 - 为什么重要?(Jonathan Aneja)来自 MSDN 上一位 VB 团队成员的博客文章。他概述了属性与字段的论点,并解释了以下琐碎的属性:

One argument I've heard for using fields over properties is that “fields are faster”, but for trivial properties that's actually not true, as the CLR's Just-In-Time (JIT) compiler will inline the property access and generate code that's as efficient as accessing a field directly.

我听说过使用字段而不是属性的一个论点是“字段更快”,但对于微不足道的属性实际上并非如此,因为 CLR 的即时 (JIT) 编译器将内联属性访问并生成如下代码与直接访问字段一样高效。

回答by Asik

It should be noted that it's possible to see the "real" performance in Visual Studio.

应该注意的是,可以在 Visual Studio 中看到“真实”的性能。

  1. Compile in Release mode with Optimisations enabled.
  2. Go to Debug -> Options and Settings, and uncheck "Suppress JIT optimization on module load (Managed only)".
  3. Optionally, uncheck "Enable Just My Code" otherwise you may not be able to step in the code.
  1. 在启用优化的发布模式下编译。
  2. 转到调试 -> 选项和设置,然后取消选中“模块加载时禁止 JIT 优化(仅限托管)”。
  3. 或者,取消选中“仅启用我的代码”,否则您可能无法输入代码。

Now the jitted assembly will be the same even with the debugger attached, allowing you to step in the optimised dissassembly if you so please. This is essential to understand how the CLR optimises code.

现在,即使附加了调试器,即时汇编也将相同,如果愿意,您可以进入优化的反汇编。这对于了解 CLR 如何优化代码至关重要。

回答by dexiang

After read all your articles, I decide to make a benchmark with these code:

阅读完您的所有文章后,我决定使用以下代码进行基准测试:

    [TestMethod]
    public void TestFieldVsProperty()
    {
        const int COUNT = 0x7fffffff;
        A a1 = new A();
        A a2 = new A();
        B b1 = new B();
        B b2 = new B();
        C c1 = new C();
        C c2 = new C();
        D d1 = new D();
        D d2 = new D();
        Stopwatch sw = new Stopwatch();

        long t1, t2, t3, t4;

        sw.Restart();
        for (int i = COUNT - 1; i >= 0; i--)
        {
            a1.P = a2.P;
        }

        sw.Stop();

        t1 = sw.ElapsedTicks;

        sw.Restart();
        for (int i = COUNT - 1; i >= 0; i--)
        {
            b1.P = b2.P;
        }

        sw.Stop();


        t2 = sw.ElapsedTicks;

        sw.Restart();
        for (int i = COUNT - 1; i >= 0; i--)
        {
            c1.P = c2.P;
        }

        sw.Stop();

        t3 = sw.ElapsedTicks;

        sw.Restart();
        for (int i = COUNT - 1; i >= 0; i--)
        {
            d1.P = d2.P;
        }

        sw.Stop();


        t4 = sw.ElapsedTicks;
        long max = Math.Max(Math.Max(t1, t2), Math.Max(t3, t4));

        Console.WriteLine($"auto: {t1}, {max * 100d / t1:00.00}%.");
        Console.WriteLine($"field: {t2}, {max * 100d / t2:00.00}%.");
        Console.WriteLine($"manual: {t3}, {max * 100d / t3:00.00}%.");
        Console.WriteLine($"no inlining: {t4}, {max * 100d / t4:00.00}%.");

    }
    class A
    {
        public double P { get; set; }
    }
    class B
    {
        public double P;
    }
    class C
    {
        private double p;
        public double P
        {
            get => p;
            set => p = value;
        }
    }
    class D
    {
        public double P
        {
            [MethodImpl(MethodImplOptions.NoInlining)]
            get;
            [MethodImpl(MethodImplOptions.NoInlining)]
            set;
        }
    }

When test in debug mode, I got this result:

在调试模式下测试时,我得到了这个结果:

auto: 35142496, 100.78%.
field: 10451823, 338.87%.
manual: 35183121, 100.67%.
no inlining: 35417844, 100.00%.

but when switch to release mode, the result is different than before.

但是当切换到释放模式时,结果与以前不同。

auto: 2161291, 873.91%.
field: 2886444, 654.36%.
manual: 2252287, 838.60%.
no inlining: 18887768, 100.00%.

seems auto property is a better way.

似乎汽车财产是一个更好的方法。