Подтвердить что ты не робот

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

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

Теперь на вопрос. Я всегда предполагал, что, поскольку С# getters/setters действительно маскируют методы, тогда чтение публичного поля должно быть быстрее, чем вызов getter.

Итак, чтобы убедиться, что я сделал тест (код ниже). Однако этот тест дает только ожидаемые результаты (т.е. Поля быстрее, чем получатели на 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;
    }
}
4b9b3361

Ответ 1

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

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

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

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

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

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

Свойства вручную:

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

Нет вставки геттера:

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

  • Скомпилировать в режиме Release с включенной оптимизацией.
  • Перейдите в раздел "Отладка" → "Параметры и настройки" и снимите флажок "Подавлять оптимизацию JIT при загрузке модуля (только для управляемого)".
  • При необходимости снимите флажок "Включить только мой код", иначе вы не сможете войти в код.

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