我一直认为,在某些情况下,铸造可能成为衡量表现的一个可衡量的障碍。当我们开始处理令人讨厌的异常投掷\捕获的不连贯网络时,情况可能会更多。
鉴于我希望在编程时创建更正确的启发式方法,我已经被提示向.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更快吗?如果是这样,多少钱?而且,如果你知道答案,为什么?
答案 0 :(得分:17)
这里的一些答案提出了基准测试,这是朝着正确方向迈出的一步,但只是旅程的第一步。
我的团队在这方面做了大量的分析和基准测试。短版本是是,在某些情况下,接口会产生较小但可衡量的性能成本。 然而实际成本取决于很多因素,包括支持多少接口,给定引用的接口数量,访问模式等等。 CLR具有很多启发式功能,旨在加快常见情况下的接口访问速度。
如果您正在对其中一种常见情况进行基准测试,但您的实际程序属于不太常见的情况,那么您的基准测试主动有害,因为它会为您提供误导性数据。
更好地对真实代码进行真实的性能测量。使用分析器,以两种方式编写代码,并查看这两种方式是否可测量,以可见且与用户相关的方式更快地重复。
至于你对投掷和捕捉的提及:投掷和捕捉的表现成本应该是无关紧要的。根据定义,例外是例外,而不是普通。此外,例外通常表明某些事情很快就会停止;通常情况下,某些事情是否会尽快停止并不重要。如果您遇到异常门控的情况,那么您需要解决更大的问题:停止抛出这么多异常。抛出的异常应该非常罕见。
答案 1 :(得分:7)
看看这里:
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
的界面会更慢(因为后者涉及装箱/拆箱),但事实并非如此:a看来具体的类/结构引用总是更快。
此外,它可能关注的对象:我认为性能不是决定是否应该使用接口的良好标准。与其他人所说的不同,差异可以忽略不计。
答案 2 :(得分:7)
你必须衡量。
但是如果施法在你的代码中成为一个(潜在的)瓶颈,你就会在问题菜单上超越意大利面。
答案 3 :(得分: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);
}
}
}
答案 4 :(得分:4)
假设没有定义静态转换运算符,强制转换应该大致相同。在类(而不是接口)上调用方法时,可能会有一些“内联”优化,但除非将方法调用 insane ,否则不会注意到这一点。
总而言之;两者都没有明显的性能问题。或者用另一种方式说:直到我描述并显示这一点,我先看看其他地方。
答案 5 :(得分:2)
首先,你不需要在这里进行转换,因为代码必须在没有强制转换的情况下工作。 IParent
是IEntity
,所以它应该可以正常工作。
施法会对性能产生影响吗?稍微如果它涉及转换(如果类型实现IConvertible
并且转换是必要的)。否则它可以忽略不计,因为所有它必须做的是进行类型检查,这应该是闪电般的。