Поле против свойства. Оптимизация производительности


обратите внимание, что этот вопрос относится только к производительности. Давайте пропустим рекомендации по дизайну, философию, совместимость, переносимость и все, что не связано с чистой производительностью. Спасибо.

теперь вопрос. Я всегда предполагал, что поскольку геттеры/сеттеры C# - это действительно замаскированные методы, то чтение публичного поля должно быть быстрее, чем вызов геттера.

поэтому, чтобы убедиться, что я сделал тест (код ниже). Однако этот тест дает только ожидаемые результаты (т. е. поля быстрее, чем геттеры на 34%)если вы запускаете его из Visual Studio.

Как только вы запустите его из командной строки, он показывает почти такое же время...

единственное объяснение может быть в том, что CLR делает дополнительную оптимизацию (поправьте меня, если я ошибаюсь здесь).

Я не верю, что в реальном приложении, где эти свойства используются гораздо более сложным образом, они будут оптимизированы таким же образом.

пожалуйста, помогите мне доказать или опровергнуть идею о том, что в реальной жизни свойства медленнее, чем поля.

вопрос в том, как я должен изменить тестовые классы, чтобы изменить поведение среды CLR, чтобы публичное поле превосходило геттеры. Или покажите мне, что любое свойство без внутренней логики будет выполнять то же самое, что и поле (по крайней мере, на геттере)

EDIT: я говорю только о выпуске x64 build.

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;
    }
}
5 58

5 ответов:

как уже упоминали другие, геттеры встроенный.

если вы хотите избежать встраивания, вы должны

  • замените автоматические свойства на ручные:

    class A 
    {
        private double p;
        public double P
        {
            get { return p; }
            set { p = value; }
        }
    } 
    
  • и скажите компилятору, чтобы он не вставлял геттер (или оба, если вам это нравится):

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

обратите внимание, что первое изменение не влияет на производительность, тогда как второе изменение показывает четкие накладные расходы на вызов метода:

механическая свойства:

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

нет встраивания геттера:

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

посмотреть свойства против полей-почему это имеет значение? (Джонатан Анея) статья в блоге от одного из членов команды VB на MSDN. Он описывает аргумент property versus fields, а также объясняет тривиальные свойства следующим образом:

один аргумент, который я слышал для использования полей над свойствами, заключается в том, что "поля быстрее", но для тривиальных свойств это на самом деле не так правда, поскольку компилятор среды CLR Just-In-Time (JIT) будет встроен свойство доступ и создание кода, который так же эффективен, как и доступ к поле сразу.

JIT встроит любой метод (а не только геттер), который его внутренние метрики определяют, будет быстрее встроен. Учитывая, что стандартное свойство return _Property; Он будет встроен в каждый случай.

причина, по которой вы видите другое поведение, заключается в том, что в режиме отладки с подключенным отладчиком JIT значительно затруднен, чтобы гарантировать, что любые местоположения стека соответствуют тому, что вы ожидаете от кода.

вы также забываете правило номер один производительность, тестирование бьет мышление. Например, хотя быстрая сортировка асимптотически быстрее, чем сортировка вставки, сортировка вставки на самом деле быстрее для чрезвычайно малых входов.

единственное объяснение может быть в том, что CLR делает дополнительную оптимизацию (correrct меня, если я ошибаюсь здесь).

Да, это называется подстановкой. Это делается в компиляторе (уровень машинного кода-т. е. JIT). Поскольку геттер / сеттер тривиальны (т. е. очень простой код), вызовы метода уничтожаются, а геттер/сеттер записывается в окружающий код.

этого не происходит в режиме отладки для поддержки отладки (т. е. возможность установить останова в геттер или сеттер).

в visual studio нет способа сделать это в отладчике. Сборка, запуск без отладчика прилагается, и вы получите полную оптимизацию.

Я не верю, что в реальном приложении, где эти свойства используются гораздо более сложным образом, они будут оптимизированы таким же образом.

мир полон иллюзий, которые ошибочны. Они будут оптимизированы, поскольку они все еще тривиальны (т. е. простой код, поэтому они кликабельны).

следует отметить, что в Visual Studio можно увидеть "реальную" производительность.

  1. компиляция в режиме выпуска с включенной оптимизацией.
  2. перейдите в Debug - > Options and Settings и снимите флажок " подавить JIT-оптимизацию при загрузке модуля (только управляемый)".
  3. дополнительно, снимите флажок "Включить только мой код" в противном случае вы не сможете войти в код.

теперь jitted сборка будет такой же даже с отладчиком прилагается, что позволяет вам шагнуть в оптимизированную сборку, если вам так угодно. Это необходимо для понимания того, как среда CLR оптимизирует код.