接口的空实现是否会降低执行速度?

时间:2013-11-01 12:32:17

标签: c# .net compiler-construction

如果我在一个类中实现一个接口,那什么都不做,它是否会减慢从中调用的代码?示例2(NoLogger)是否会对代码的速度产生任何影响,它会在?

中使用

示例代码:

interface ILogger{
    void Write(string text);
}

class TextLogger : ILogger {
    public void Write(string text){
        using (var sw = new StreamWriter(@"C:\log.txt"))
        {
            sw.WriteLine(text);
        }
    }
}

class NoLogger : ILogger{
    public void Write(string text){
        //Do absolutely nothing
    }
}

实施1,TextLogger

void Main(){
        ILogger tl = new TextLogger();
        for (int i = 0; i < 100; i++)
        {
            tl.Write(i.ToString());
        }
 }

实施2,NoLogger

void Main(){
        ILogger tl = new NoLogger();
        for (int i = 0; i < 100; i++)
        {
            tl.Write(i.ToString());
        }
 }

当然,示例1(textlogger)会降低其实现代码的执行速度,因为它实际上有所作为。

但是例子2怎么样?编译器是否足够聪明,即使实例化一个类并且调用了一个方法,也绝对没有代码可以在任何路径上执行任何操作,只是在编译时忽略它?

3 个答案:

答案 0 :(得分:2)

认为我们可以将此概括为“JIT是否会内联接口方法的虚拟调用” - 我强烈怀疑答案是“否”(为此,它会需要证明所涉及的实现类型只能是一种具体类型,而且这种分析比我预期的要多得多。

当然,您可以通过电话运行500,000,000次,并且没有任何内容 - 然后您可以在答案中找到合理的起始位置

另请注意:即使Write没有做任何事情:仍然需要执行i.ToString(),因为可能会产生副作用

我怀疑您应该查看[Conditional("...")]属性。这个会改变。很多。例如:

public static class Logger
{
    [Conditional("TRACE")]
    public static void Write(string text)
    {
       // some stuff
    }
}

现在;如果我们编译 而不 ,则定义TRACE符号:

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
        Logger.Write(i.ToString());
    }
 }

我们的代码编译就像是:

static void Main()
{
    for (int i = 0; i < 100; i++)
    {
    }
 }

调用已被删除,但同时:删除了任何参数评估(i.ToString() 。如果我们使用定义的TRACE符号进行编译 - 则代码存在。

答案 1 :(得分:2)

  

编译器是否足够聪明,即使实例化一个类并且调用了一个方法,也绝对没有代码可以在任何路径上执行任何操作,只是在编译时忽略它?

编译器无法合法地完全取消呼叫。至少,编译器必须评估您传递给被调用方法的所有表达式 - 具体来说,在您的示例中i.ToString()将被调用100次,而不管实现的实际操作是什么。编译器无法对其进行优化,否则在参数表达式具有副作用的情况下,它可能会意外地更改程序的语义。

答案 2 :(得分:2)

有一个interesting blog entry here描述了.net JIT编译器进行动态分析,以确定你是否反复使用相同的虚函数,然后进行虚拟化并可能内联它。为此,在应用程序运行时修补代码。

因此它不会评估为无操作,但JIT编译器将消除与调用方法相关联的大部分开销。