在我目前的代码库中,复杂的if
语句通常会被委托调用替换。由于代码的结构,在应用程序的过程中将多次调用相同的委托。例如,
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;
}
}
由于性能是一个问题,我想知道JITter是否最终会意识到每次调用其中一种方法并内联或以其他方式优化呼叫。
那么,可以C#JITter内联还是优化重复的委托调用?
答案 0 :(得分:5)
没有。委托调用始终是间接调用,并在运行时动态绑定。这在调用时发生,然后才知道委托对象的值。抖动在此之前运行,优化器无法改进它们,它完全不知道将要调用什么。
请注意,如何知道目标方法是实例方法还是静态方法,它假定实例方法。如果调用存根是静态的,则需要额外的工作来重新调用调用堆栈。对于x64代码来说,额外的工作更为重要。值得注意的是,静态方法通常会更有意义,所以要注意这一点。
首次调用是昂贵的,当创建调用存根并且需要对目标方法进行jitted时。由于在这种情况下抖动已经正确地猜测了方法的外观,因此存根只是一个JMP指令。在此之后的任何呼叫以正常速度运行,并且超出JMP,将不会比正常呼叫慢得多,尽管您不能像这些小方法那样通过内联而受益。没有进行任何更改,.NET没有像热点编译器那样的东西,因为它没有使用解释器。
您必须进行衡量,请注意这是非常快速的代码,因此简单的基准错误往往会受到影响,并且测量结果不会非常一致。但是只有当你发现你有一个实际的性能问题时,才考虑花时间。这种情况并不常见,代表们不要吮吸。