C#JITter可以优化重复的委托调用吗?
问题描述:
在我当前的代码库中,复杂的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内嵌或以其他方式优化重复委托调用?
答
它没有。委托调用始终是间接调用,并在运行时动态绑定。这发生在进行调用时,只有那个代表对象的值已知。抖动在此之前运行,优化器没有任何东西可以改善它们,它不知道究竟会调用什么。
注意它如何无法知道目标方法是实例方法还是静态方法,它假定实例方法。如果调用存根是静态的,则需要额外的工作来调用调用堆栈。对于x64代码,额外的工作更为重要。在片段中值得注意的是静态方法通常会更有意义,因此请注意这一点。
第一次调用很昂贵,这是当调用存根被创建并且需要调用目标方法时。因为在这种情况下,抖动已经正确地猜测了该方法的外观,所以存根就是单个JMP指令。在此之后的任何调用以正常速度运行,超出JMP的速度将不会比普通调用慢很多,尽管您不能像这些小型方法通常那样从内联中受益。 .NET没有做任何更改,因为它没有使用解释器,所以.NET没有像热点编译器那样的东西。
您必须测量,注意这是非常快速的代码,如此简单的基准测试错误往往会受到伤害,测量结果也不会很一致。但是,只有当你发现你有一个实际的性能问题时才考虑花费时间。这并不常见,代表们不会吝啬。
您如何对它进行基准测试以了解您的基于委托的调度具有哪种方式?另外,使用方法组的afaik比使用lambda表达式更昂贵,所以我建议尝试'_exampleMethod =(a,b)=>加法器(a,b);'。但是,你又对perf的好奇心好吗?基准! – MarcinJuraszek
@MarcinJuraszek就你对lambda表达的评论而言,实际的方法要复杂得多,通常在25行左右。我可能会做一些性能基准测试,以便稍后看到实际的性能影响,但我也对幕后技术发生的情况感兴趣,而不仅仅是实际的性能影响。谢谢。 – Defenestrator
@MarcinJuraszek你为什么认为创建一个新的方法,除了调用另一个方法,传递完全相同的参数以及不做任何有效的工作会更有效率? – Servy