Литье в литье по сравнению с классом
Я был убежден, что кастинг может при определенных обстоятельствах стать измеримым препятствием на производительность. Это может быть и в том случае, когда мы начинаем иметь дело с некогерентными сетями противного исключения, бросая\ловить.
Учитывая, что я хочу создать более правильную эвристику, когда дело доходит до программирования, мне было предложено задать этот вопрос для гуру .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? Если да, то на сколько? И, если вы знаете ответ, почему?
Ответы
Ответ 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
и требуется преобразование). В противном случае это пренебрежимо малый, поскольку все, что ему нужно сделать, это выполнить проверку типа, которая должна быть молниеносной.