.NET Свойства против полей. Что быстрее?

Третьего дня писал очень ресурсоёмкий алгоритм. Он был узким местом во время работы приложения, и, соответственно, его скорость работы серьезным образом отражалась на производительности всего приложения. Запустив профайлер заметил, что основная нагрузка приходилась на получение значений из свойств. Не долго думая, я заменил автоматические свойства полями. Профайлер показал значительный прирост производительности. На этом я и успокоился.

Однако этот факт не давал мне покоя — как же так, я везде и всегда старался следовать правилам написания кода, а тут оказывается что этим самым рубил его производительность? Я решил написать небольшой тест для оценки скорости работы свойств и полей. Вот что у меня получилось.

using System;
using System.Diagnostics;

namespace PropertyBenchmark
{
    static class Program
    {
        private static readonly Stopwatch Stopwatch = new Stopwatch();

        ///
        /// The main entry point for the application.
        ///
        [STAThread]
        private static void Main()
        {
            var filedclass = new FieldClass();
            var propclass = new PropertyClass();

            int passes = 10000000;
            int takes = 8;

            Test(delegate { }, "Empty", takes, passes);
            Test(delegate { }, "Empty", takes, passes);

            Test(delegate { int val = filedclass.Field; }, "Field read", takes, passes);
            Test(delegate { int val = propclass.Property; }, "Property read", takes, passes);

            Test(delegate { filedclass.Field = 5; }, "Field write", takes, passes);
            Test(delegate { propclass.Property = 5; }, "Property write", takes, passes);

            Test(delegate { filedclass.Field++; }, "Field increment", takes, passes);
            Test(delegate { propclass.Property++; }, "Property increment", takes, passes);

            Console.ReadLine();
        }

        private static void Test(Action action, string name, int takes, int passes)
        {
            Console.WriteLine("Test " + name);

            for (int i = 0; i < takes; i++)
            {
                Stopwatch.Restart();

                for (int j = 0; j < passes; j++)
                {
                    action();
                }

                Stopwatch.Stop();

                Console.Write(string.Format("{1} ms; ", i, Stopwatch.ElapsedTicks/TimeSpan.TicksPerMillisecond));
            }
            Console.WriteLine();
            Console.WriteLine();
        }
    }

    public class PropertyClass
    {
        public int Property { get; set; }
    }

    public class FieldClass
    {
        public int Field;
    }
}

Тест прогоняет различные операции с полями и свойствами в 8 подходов по 10 000 000 раз. Результаты: (конфигурация Debug / без отладчика)

Test Empty
13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms;

Test Empty
13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms; 13 ms;

Test Field read
18 ms; 18 ms; 19 ms; 18 ms; 18 ms; 18 ms; 21 ms; 18 ms;

Test Property read
29 ms; 29 ms; 30 ms; 30 ms; 30 ms; 29 ms; 30 ms; 30 ms;

Test Field write
18 ms; 18 ms; 18 ms; 18 ms; 19 ms; 18 ms; 18 ms; 18 ms;

Test Property write
26 ms; 26 ms; 26 ms; 26 ms; 27 ms; 27 ms; 26 ms; 26 ms;

Test Field increment
20 ms; 20 ms; 19 ms; 19 ms; 19 ms; 20 ms; 19 ms; 20 ms;

Test Property increment
41 ms; 41 ms; 41 ms; 41 ms; 41 ms; 41 ms; 41 ms; 41 ms;

Очевидное превосходство полей над свойствами (почти в 2 раза!)
А теперь попробуем запустить тот же код в конфигурации Release без отладчика:

Test Empty
9 ms; 10 ms; 10 ms; 10 ms; 10 ms; 10 ms; 10 ms; 9 ms;

Test Empty
10 ms; 9 ms; 10 ms; 9 ms; 9 ms; 9 ms; 9 ms; 9 ms;

Test Field read
7 ms; 7 ms; 7 ms; 7 ms; 7 ms; 7 ms; 7 ms; 7 ms;

Test Property read
7 ms; 7 ms; 7 ms; 7 ms; 7 ms; 8 ms; 7 ms; 7 ms;

Test Field write
7 ms; 7 ms; 8 ms; 8 ms; 7 ms; 7 ms; 7 ms; 7 ms;

Test Property write
7 ms; 7 ms; 7 ms; 8 ms; 7 ms; 7 ms; 7 ms; 8 ms;

Test Field increment
8 ms; 7 ms; 7 ms; 8 ms; 8 ms; 8 ms; 8 ms; 8 ms;

Test Property increment
8 ms; 8 ms; 8 ms; 8 ms; 8 ms; 8 ms; 8 ms; 7 ms;

Как видим, никакой ощутимой разницы в производительности нет. Можно свободно использовать свойства, как и рекомендует Microsoft 🙂

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

 

  • justserega

    Хм, отлично! Тоже всегда делал свойства и всегда думал не падает ли производительность 🙂 Спасибо, что развеял сомнения 🙂

  • justserega

    Ещё было бы очень интересно посмотреть сравнение чистого вызова функции/виртуального/через событие/лямбда 🙂