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

Литье в литье по сравнению с классом

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

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

Чтобы дать пример кода, предположим, что это существует:

public interface IEntity { IParent DaddyMommy { get; } }
public interface IParent : IEntity { }
public class Parent : Entity, IParent { }
public class Entity : IEntity
{
    public IParent DaddyMommy { get; protected set; }
    public IParent AdamEve_Interfaces
    {
        get
        {
            IEntity e = this;
            while (e.DaddyMommy != null)
                e = e.DaddyMommy as IEntity;
            return e as IParent;
        }   
    }
    public Parent AdamEve_Classes
    {
        get
        {
            Entity e = this;
            while (e.DaddyMommy != null)
                e = e.DaddyMommy as Entity;
            return e as Parent;
        }
    }
}

Итак, это AdamEve_Interfaces быстрее, чем AdamEve_Classes? Если да, то на сколько? И, если вы знаете ответ, почему?

4b9b3361

Ответ 1

Взгляните сюда:

http://thatstoday.com/robbanp/blog/6/25/csharp-performance--cast-vs-interface

И да, кажется, вы правы.

Изменить Ну, похоже, я был неправ. И как мой "patrício" Martinho Fernandes прокомментировал ниже, вышеупомянутая ссылка полностью фиктивная (но я сохраню ее здесь, ради честного редактирования).

В настоящее время у меня есть свободное время, поэтому я написал простой код измерения производительности:

public partial class Form1 : Form
{
    private const int Cycles = 10000000;

    public interface IMyInterface
    {
        int SameProperty { get; set; }
    }

    public class InterfacedClass : IMyInterface
    {
        public int SameProperty { get; set; }
    }

    public class SimpleClass
    {
        public int SameProperty { get; set; }
    }

    public struct InterfacedStruct : IMyInterface
    {
        public int SameProperty { get; set; }
    }

    public struct SimpleStruct
    {
        public int SameProperty { get; set; }
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
        var simpleClassTime = MeasureSimpleClass();
        var interfacedClassTime = MeasureInterfacedClass();
        var simpleStructTime = MeasureSimpleStruct();
        var interfacedStructTime = MeasureInterfacedStruct();

        var message = string.Format(
            "simpleClassTime = {0}\r\ninterfacedClassTime = {1}\r\nsimpleStructTime = {2}\r\ninterfacedStructTime = {3}",
            simpleClassTime,
            interfacedClassTime,
            simpleStructTime,
            interfacedStructTime
        );

        textBox.Text = message;
    }

    private static long MeasureSimpleClass() {
        var watch = Stopwatch.StartNew();
        var obj = new SimpleClass();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureInterfacedClass() {
        var watch = Stopwatch.StartNew();
        IMyInterface obj = new InterfacedClass();

        for (var i = 0; i < Cycles; i++) {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureSimpleStruct()
    {
        var watch = Stopwatch.StartNew();
        var obj = new SimpleStruct();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureInterfacedStruct()
    {
        var watch = Stopwatch.StartNew();
        IMyInterface obj = new InterfacedStruct();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }
}

И результат:

simpleClassTime = 274
interfacedClassTime = 339
simpleStructTime = 247
interfacedStructTime = 302

Я действительно думал, что интерфейс будет быстрее для типов class и медленнее для struct (поскольку во втором случае задействован бокс /unboxing ), но это не так: конкретный класс/структура Кажется, ссылка всегда быстрее.

Кроме того, к кому это может относиться: я считаю, что производительность не является хорошим критерием для определения того, следует ли использовать интерфейс или не использовать его. Разница в том, что, как говорят другие, здесь ничтожно.

Ответ 2

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

Моя команда провела большую часть профилирования и бенчмаркинга в этой области. Короткий вариант - да, бывают ситуации, когда интерфейсы накладывают небольшую, но измеримую стоимость исполнения. Однако фактическая стоимость зависит от множества факторов, в том числе от того, сколько интерфейсов поддерживается, сколько из этих интерфейсов относится к данной ссылке, к какому шаблону доступа и т.д. CLR имеет множество эвристик, предназначенных для ускорения доступа к интерфейсу в обычных случаях.

Если вы сравниваете один из этих распространенных случаев, но ваша фактическая программа попадает в менее распространенный случай, тогда ваш бенчмаркинг активно вреден, поскольку он дает вам данные, вводящие в заблуждение.

Намного лучше делать реалистичные измерения производительности на реальном коде. Используйте профилировщик, напишите код в обоих направлениях и посмотрите, измеряется ли он в любом направлении, повторяемо быстрее, что является видимым и релевантным для пользователя.

Что касается вашей ссылки на метание и ловить: стоимость выполнения метания и лова должна быть неактуальной. Исключения по определению являются исключительными, а не обычными. Кроме того, исключения обычно указывают на то, что что-то скоро остановится; обычно неважно, что-то останавливается как можно быстрее. Если вы находитесь в ситуации, когда ваша производительность ограничена исключениями, тогда у вас есть больше проблем для решения: прекратите бросать так много исключений. Исключенное исключение должно быть крайне редким.

Ответ 3

Вы должны были бы измерить.

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

Ответ 4

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

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

Ответ 5

Вы пробовали проверить это? Здесь цикл, который работает 10 000 000 раз. На моей машине версия интерфейса занимает около 440 мс, а версия класса - 410 мс. Так что довольно близко, но в целом выигрывает классная версия.

using System;

namespace ConsoleApplication1
{
    public interface IEntity { IParent DaddyMommy { get; } }
    public interface IParent : IEntity { }
    public class Parent : Entity, IParent { }
    public class Entity : IEntity
    {
        public IParent DaddyMommy { get; protected set; }
        public IParent AdamEve_Interfaces
        {
            get
            {
                IEntity e = this;
                while (this.DaddyMommy != null)
                    e = e.DaddyMommy as IEntity;
                return e as IParent;
            }
        }
        public Parent AdamEve_Classes
        {
            get
            {
                Entity e = this;
                while (this.DaddyMommy != null)
                    e = e.DaddyMommy as Entity;
                return e as Parent;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Entity X = new Entity();
            Parent P;
            IParent IP;
            System.Diagnostics.Stopwatch ST = new System.Diagnostics.Stopwatch();
            Int32 i;

            ST.Start();
            for (i = 0; i < 10000000; i++)
            {
                IP = X.AdamEve_Interfaces;
            }
            ST.Stop();
            System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds);

            ST.Reset();

            ST.Start();
            for (i = 0; i < 10000000; i++)
            {
                P = X.AdamEve_Classes;
            }
            ST.Stop();
            System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds);
        }
    }

}

Ответ 6

Прежде всего, вам не нужно кастинг здесь, так как код должен работать без кастинга. IParent - это IEntity, поэтому он должен работать.

Оказывает ли литье влияние на производительность? Немного, если это связано с преобразованием (если тип реализует IConvertible и требуется преобразование). В противном случае это пренебрежимо малый, поскольку все, что ему нужно сделать, это выполнить проверку типа, которая должна быть молниеносной.