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

Compiler optimization of properties that remain static for the duration of a loop

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

If you use data that is static for the duration of the loop, obtain it before the loop instead of repeatedly accessing a field or property.

以下代码作为示例给出:

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

变成

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]);
}

文章指出:

Note that if these are fields, it may be possible for the compiler to do this optimization automatically. If they are properties, it is much less likely. If the properties are virtual, it cannot be done automatically.

为什么 "much less likely" 编译器以这种方式优化属性,什么时候可以期望某个特定的 属性 被优化或不被优化?我会假设在访问器中执行额外操作的属性对于编译器来说更难优化,而那些只修改支持字段的属性更有可能被优化,但想要一些更具体的规则。 auto-implemented 属性总是优化的吗?

Why is it "much less likely" for properties to be optimized by the compiler in this manner, and when can one expect for a particular property to be or not to be optimized?

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

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

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

需要抖动应用两个优化:

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

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

其次,必须将字段访问代码提升到循环体之外。称为 "invariant code motion" 的优化。当抖动可以证明循环体内的语句不影响值时,适用于简单的 属性 getter 代码。

抖动优化器实现了它,但它并不出色。在这种特殊情况下,它很可能会在无法内联 CalculateTax() 方法时放弃。本机编译器对其进行更积极的优化,它可以负担得起消耗内存和分析时间。抖动优化器必须满足一个非常严格的截止日期以避免暂停。

当您自己执行此操作时,请牢记优化器的限制。如果这些方法 do 有您没有预料到的副作用,那当然是一个非常丑陋的错误。并且 只有 当探查器告诉您此代码在热路径上时才执行此操作,通常约 10% 的代码会实际影响执行时间。这里的几率很低,获取 customer/order 数据的数据库查询比计算税收要贵几个数量级。幸运的是,像这样的代码转换也往往使代码更具可读性,因此您通常可以免费获得它。 YMMV.

关于抖动优化的背景资料is here