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

C# 编译器优化在循环期间保持静态的属性,c#,optimization,compiler-optimization,C#,Optimization,Compiler Optimization,我在看书。标题为“避免重复访问字段或属性”的部分包含一条准则: 如果在循环期间使用静态数据,请获取它 而不是重复访问字段或属性 以下代码作为示例给出: for (int item = 0; item < Customer.Orders.Count; item++) { CalculateTax(Customer.State, Customer.Zip, Customer.Orders[item]); } for(int item=0;item

我在看书。标题为“避免重复访问字段或属性”的部分包含一条准则:

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

以下代码作为示例给出:

for (int item = 0; item < Customer.Orders.Count; item++)
{
   CalculateTax(Customer.State, Customer.Zip, Customer.Orders[item]);
}
for(int item=0;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]);
}
string state=Customer.state;
字符串zip=Customer.zip;
int count=Customers.Orders.count;
对于(int item=0;item
该条规定:

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

为什么编译器以这种方式对属性进行优化的“可能性要小得多”,以及何时可以对特定属性进行优化或不进行优化?我假设在访问器中执行附加操作的属性对于编译器来说更难优化,而那些只修改支持字段的属性更有可能被优化,但需要一些更具体的规则。自动实现的属性是否始终优化

为什么编译器以这种方式对属性进行优化的“可能性要小得多”,以及何时可以对特定属性进行优化或不进行优化

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

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

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

它需要抖动来应用两种优化:

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

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

其次,必须将现场访问代码从回路体中吊出。一种称为“不变代码运动”的优化。当抖动可以证明循环体中的语句不影响值时,可以处理简单的属性getter代码

抖动优化器实现了它,但它不是一流的。在这种特殊情况下,当它无法内联CalculateTax()方法时,它很可能会放弃。本机编译器会更积极地对其进行优化,这样可以节省内存和分析时间。抖动优化器必须满足一个非常严格的期限,以避免暂停

当您自己这样做时,一定要记住优化器的约束。当然,如果这些方法确实有副作用,而你并没有指望这些副作用的话,那就是一个非常丑陋的错误。而只有当探查器告诉您此代码位于热路径上时才执行此操作,通常约10%的代码会影响执行时间。在这种情况下,获取客户/订单数据的dbase查询要比计算税收高出几个数量级。幸运的是,像这样的代码转换也会使代码更具可读性,所以您通常可以免费获得它。YMMV


抖动优化的背景资料。

即使它只返回支持字段的值,编译器也需要证明支持字段在整个循环中永远不会改变,而这在大多数情况下是很难证明的(通常是不可能的)。@Servy:是的,但我相信这与
的情况大致相同。请注意,如果这些字段是字段,编译器可能会自动执行此优化
,如果编译器可以证明该属性只返回字段的值,那么它是相同的。它可能知道也可能不知道该属性是否正是这样做的。@EricJ。这当然是有道理的。不过,极端示例存在一个问题,因为它不是此优化所适用的一段代码(编码指南要求它是一个在循环持续时间内保持静态的属性,否则开发人员在此无需执行任何操作)@OwenPauling:问题是编译器要证明它在循环期间保持静态。编译器必须了解rnd.Next()是否会在循环期间保持静态(人类很容易知道它不会,但在一般情况下,这对编译器来说是一项艰巨的任务)。