2017-10-20 138 views
1

在我当前的代码库中,复杂的if语句经常被委托调用取代。由于代码的结构,在应用程序的过程中会多次调用相同的委托。例如,C#JITter可以优化重复的委托调用吗?

class ExampleClass 
{ 
    private delegate double ExampleDelegate(double x, double y); 
    private ExampleDelegate _exampleMethod; 
    private bool _condition1; 

    ... 

    public double ApiFunction(List<double> a, List<double> b, bool condition2) 
    { 
     if ((_condition1 && !condition2) || getCondition3()) 
     { 
      _exampleMethod = adder; 
     } 
     else 
     { 
      _exampleMethod = subtracter; 
     } 

     double finalResult = 0; 

     for (int i = 0; i < a.Count; i++) 
     { 
      finalResult += _exampleMethod(a[i], b[i]); 
     } 

     return finalResult; 
    } 

    private double adder(double a, double b) 
    { 
     return a + b; 
    } 

    private double subtracter(double a, double b) 
    { 
     return a - b; 
    } 
} 

由于性能是一个问题在这里,我想知道,如果抖动最终会认识到,这些方法之一是被称为每次和内嵌或以其他方式优化呼叫。

那么,可以C#JITter内嵌或以其他方式优化重复委托调用

+0

您如何对它进行基准测试以了解您的基于委托的调度具有哪种方式?另外,使用方法组的afaik比使用lambda表达式更昂贵,所以我建议尝试'_exampleMethod =(a,b)=>加法器(a,b);'。但是,你又对perf的好奇心好吗?基准! – MarcinJuraszek

+0

@MarcinJuraszek就你对lambda表达的评论而言,实际的方法要复杂得多,通常在25行左右。我可能会做一些性能基准测试,以便稍后看到实际的性能影响,但我也对幕后技术发生的情况感兴趣,而不仅仅是实际的性能影响。谢谢。 – Defenestrator

+0

@MarcinJuraszek你为什么认为创建一个新的方法,除了调用另一个方法,传递完全相同的参数以及不做任何有效的工作会更有效率? – Servy

回答

4

它没有。委托调用始终是间接调用,并在运行时动态绑定。这发生在进行调用时,只有那个代表对象的值已知。抖动在此之前运行,优化器没有任何东西可以改善它们,它不知道究竟会调用什么。

注意它如何无法知道目标方法是实例方法还是静态方法,它假定实例方法。如果调用存根是静态的,则需要额外的工作来调用调用堆栈。对于x64代码,额外的工作更为重要。在片段中值得注意的是静态方法通常会更有意义,因此请注意这一点。

第一次调用很昂贵,这是当调用存根被创建并且需要调用目标方法时。因为在这种情况下,抖动已经正确地猜测了该方法的外观,所以存根就是单个JMP指令。在此之后的任何调用以正常速度运行,超出JMP的速度将不会比普通调用慢很多,尽管您不能像这些小型方法通常那样从内联中受益。 .NET没有做任何更改,因为它没有使用解释器,所以.NET没有像热点编译器那样的东西。

您必须测量,注意这是非常快速的代码,如此简单的基准测试错误往往会受到伤害,测量结果也不会很一致。但是,只有当你发现你有一个实际的性能问题时才考虑花费时间。这并不常见,代表们不会吝啬。