编译器优化在循环期间保持静态的属性

时间:2016-01-29 20:07:56

标签: c# optimization compiler-optimization

我正在阅读Improving .NET Application Performance and Scalability。标题为避免重复字段或属性访问的部分包含一个准则:

  

如果您在循环期间使用静态数据,请获取它   在循环之前,而不是重复访问字段或属性。

以下代码作为示例:

string state = Customer.State;
string zip = Customer.Zip;
int count = Customers.Orders.Count;
for (int item = 0; item < count; item++)
{
   CalculateTax(state, zip, Customer.Orders[item]);
}

变为

{{1}}

文章指出:

  

请注意,如果这些是字段,则编译器可能会这样做   自动执行此优化。如果它们是属性,那就太多了   不太可能。如果属性是虚拟的,则无法完成   自动。

为什么它不太可能&#34;对于要由编译器以这种方式优化的属性,何时可以预期特定属性是否被优化?我假设在访问器中执行附加操作的属性对于编译器来说更难以优化,并且那些仅修改后备字段的属性更可能被优化,但是需要一些更具体的规则。自动实现的属性是否始终优化?

2 个答案:

答案 0 :(得分:4)

  

为什么编译器以这种方式优化属性“不太可能”,何时可以预期特定属性是否被优化?

属性并不总是字段的包装器。如果属性中存在任何程度的逻辑,则编译器很难证明重新使用循环开始时首先获得的值是正确的。

作为一个极端的例子,请考虑

private Random rnd = new Random();
public int MyProperty
{
    get { return rnd.Next(); }
}

答案 1 :(得分:3)

它需要抖动才能应用两个优化:

首先,必须内联属性getter方法,使其变为等效的字段访问。当getter很小并且不会抛出异常时,这往往会起作用。这是必要的,因此优化器可以确保getter不依赖于可能受其他代码影响的状态。

请注意,如果Customer.Orders []索引器改变Customer.State属性,那么手动优化代码将是错误的。像这样的惰性代码当然不太可能,但它从来没有这样做过:)优化器必须确定。

其次,必须从循环体中提取字段访问代码。一种称为“不变代码运动”的优化。当抖动可以证明循环体内的语句不影响值时,可以使用简单的属性getter代码。

抖动优化器实现了它,但它并不是很好。在这种特殊情况下,当它无法内联CalculateTax()方法时,它很可能会放弃。本机编译器可以更加积极地优化它,它可以在内存上刻录内存和分析时间。抖动优化器必须满足相当困难的期限才能避免暂停。

当您自己执行此操作时,请记住优化程序的约束。当然,如果这些方法具有您不依赖的副作用,那么相当丑陋的bug。当分析器告诉您此代码位于热路径上时,执行此操作,典型的~10%的代码实际上会影响执行时间。这里的概率很低,用于获取客户/订单数据的dbase查询比计算税收要贵几个数量级。幸运的是,像这样的代码转换也会使代码更具可读性,因此您通常可以免费获得代码。 YMMV。

有关抖动优化的背景信息is here