简体   繁体   English

界面强制转换与类强制转换

[英]Interface Casting vs. Class Casting

I've been led to believe that casting can, in certain circumstances, become a measurable hindrance on performance. 我被引导相信在某些情况下,铸造可能成为可衡量的性能障碍。 This may be moreso the case when we start dealing with incoherent webs of nasty exception throwing\\catching. 当我们开始处理令人讨厌的异常投掷\\捕获的不连贯网络时,情况可能会更多。

Given that I wish to create more correct heuristics when it comes to programming, I've been prompted to ask this question to the .NET gurus out there: Is interface casting faster than class casting? 鉴于我希望在编程时创建更正确的启发式方法,我已经被提示向.NET专家提出这个问题:接口是否比类型转换更快?

To give a code example, let's say this exists: 为了给出一个代码示例,让我们说这存在:

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;
        }
    }
}

So, is AdamEve_Interfaces faster than AdamEve_Classes? 那么,AdamEve_Interfaces比AdamEve_Classes快吗? If so, by how much? 如果是这样,多少钱? And, if you know the answer, why? 而且,如果你知道答案,为什么?

A number of the answers here suggest benchmarking, which is a step in the right direction, but only the first step in the journey. 这里的一些答案提出了基准测试,这是朝着正确方向迈出的一步,但这只是旅程的第一步。

My team has done a great deal of profiling and benchmarking in this area. 我的团队在这方面做了大量的分析和基准测试。 The short version is yes , there are situations in which interfaces impose a small but measurable performance cost. 简短版本是肯定的 ,在某些情况下,接口会产生较小但可衡量的性能成本。 However the actual cost depends on a great many factors, including how many interfaces are supported, how many of those interfaces a given reference is cast to, what the pattern of accesses are, and so on. 然而 ,实际成本取决于很多因素,包括支持多少接口,给定引用的接口数量,访问模式等等。 The CLR has a great many heuristics in place designed to speed up interface access in common cases. CLR具有很多启发式功能,旨在加快常见情况下的接口访问速度。

If you are benchmarking one of those common cases, but your actual program falls into a less common case, then your benchmarking is actively harmful because it is giving you data that is misleading. 如果您正在对其中一个常见案例进行基准测试,但您的实际程序属于不太常见的情况,那么您的基准测试是有害的,因为它会为您提供误导性数据。

Far better to do realistic performance measurements on real code. 真实代码上进行真实的性能测量要好得多。 Use a profiler, write the code both ways, and see whether either way is measurably, repeatably faster in a way that is visible and relevant to the user. 使用分析器,双向编写代码,并以可见且与用户相关的方式查看这两种方式是否可测量,重复性更快。

As for your reference to throwing and catching: the performance cost of throwing and catching should be irrelevant. 至于你对投掷和捕捉的提及:投掷和捕捉的表现成本应该是无关紧要的。 Exceptions are by definition exceptional , not common . 根据定义,例外是例外的 ,不常见 Furthermore, exceptions usually indicate that something is going to halt shortly; 此外,例外通常表明某些事情很快就会停止; it usually doesn't matter whether something halts as fast as possible. 通常情况下,某些事情是否会尽快停止并不重要。 If you are in a situation where your performance is gated by exceptions then you have bigger problems to solve: stop throwing so many exceptions . 如果您遇到异常门控的情况,那么您需要解决更大的问题: 停止抛出这么多异常 An exception thrown should be extremely rare. 抛出的异常应该非常罕见。

Take a look at here: 看看这里:

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

And, yes, you seem to be right. 而且,是的,你似乎是对的。

Edit Well, it seems that I was wrong. 编辑好吧,似乎我错了。 And like my "patrício" Martinho Fernandes commented bellow, the above link is completely bogus (but I'll keep it here, for the sake of honest editing). 就像我的“patrício”Martinho Fernandes评论一样,上面的链接完全是假的(但为了诚实的编辑,我会把它保留在这里)。

I do have some spare time nowadays, so I've written a simple performance measuring code: 我现在有空闲时间,所以我写了一个简单的性能测量代码:

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;
    }
}

And the result is: 结果是:

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

I really used to think that an interface would be faster for class types, and slower for struct (since boxing/unboxing is involved in the latter), but that is not the case: a concrete class/struct reference is always faster, it seems. 我以前认为接口对于class类型会更快,而对于struct更慢(因为后者涉及装箱/拆箱),但事实并非如此:具体的类/结构引用总是更快,似乎。

Also, to whom it may concern: I believe that performance is not a good criteria for deciding if an interface should or should not be used. 此外,它可能涉及到谁:我认为性能不是决定是否应该使用接口的良好标准。 The difference is, like others said here, negligible. 与其他人所说的不同的是,差别可以忽略不计。

You would have to measure. 你必须衡量。

But if casting is becoming a (potential) bottleneck in your code you're way past spaghetti on the problem menu. 但是如果你的代码成为一个(潜在的)瓶颈,你就会在问题菜单上超越意大利面。

Have you tried testing it? 你试过测试了吗? Here's a loop that runs 10,000,000 times. 这是一个运行10,000,000次的循环。 On my machine the interface version takes about 440 ms and the class version about 410 ms. 在我的机器上,接口版本大约需要440毫秒,类版本大约需要410毫秒。 So pretty close but overall the class version wins. 非常接近,但总体来说,班级版本获胜。

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);
        }
    }

}

Assuming there are no static conversion operators defined, the cast should take the same time, roughly. 假设没有定义静态转换运算符, 铸造应采取的同时,大致。 There are potentially some "inlining" optimisations possible when invoking a method on a class rather than an interface, but that won't be noticed unless calling a method an insane number of times. 有可能的一些“内联”的优化可能在一类,而不是一个接口调用方法时,但不会,除非调用一个方法的时候一个疯狂的数字被注意到。

Over all; 总体; there is no significant performance issue with either. 两者都没有明显的性能问题。 Or to put it another way: until I've profiled and shown this to matter, I'd look elsewhere first. 换句话说:直到我描述并表明这一点,我先看看其他地方。

First of all, you do not need casting here as the code must work without casting. 首先,你不需要在这里进行转换,因为代码必须在没有强制转换的情况下工作。 IParent is an IEntity so it should just work. IParent是一个IEntity所以它应该工作。

Does casting has an impact on performance? 铸造会对性能产生影响吗? Slightly if it involves converting (if type implements IConvertible and conversion is necessary). 稍微如果它涉及转换(如果类型实现IConvertible和转换是必要的)。 Otherwise it is negligible since all it has to do is to do a type check which should be lightning fast. 否则它可以忽略不计,因为所有它必须做的是进行类型检查 ,它应该是闪电般快速的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM