请忽略此问题中的代码可读性。
从性能上来说,下面的代码是不是应该这样写:
int maxResults = criteria.MaxResults;
if (maxResults > 0)
{
while (accounts.Count > maxResults)
accounts.RemoveAt(maxResults);
}
或者像这样:
if (criteria.MaxResults > 0)
{
while (accounts.Count > criteria.MaxResults)
accounts.RemoveAt(criteria.MaxResults);
}
?
编辑:criteria
是一个类
,MaxResults
是一个简单的整数属性(即,public int MaxResults { get {返回 _maxResults;}}
。
C# 编译器是否将 MaxResults
视为黑盒并每次都对其求值?或者它是否足够聪明,可以弄清楚我对同一个属性进行了 3 次调用,而调用之间没有修改该属性?如果 MaxResults
是一个字段会怎么样?
优化法则之一是预先计算,所以我本能地像第一个列表一样编写了这段代码,但我很好奇这种事情是否会自动为我完成(再次忽略代码可读性)。
(注意:我对听到“微优化”的论点不感兴趣,这在我发布的特定案例中可能是有效的。我只是想了解正在发生或未发生的事情背后的一些理论。 )
最佳答案
首先,真正回答性能问题的唯一方法是实际尝试这两种方法并在现实条件下测试结果。
也就是说,说“编译器”不进行此优化的其他答案是因为该属性可能有副作用,这既是对又是错。这个问题的问题(除了根本问题,即如果不实际尝试并测量结果就无法回答)是“编译器”实际上是两个编译器:编译为 MSIL 的 C# 编译器和 JIT 编译器,它将 IL 编译为机器代码。
C# 编译器从不进行这种优化;如前所述,这样做需要编译器查看被调用的代码,并验证它计算的结果在被调用者代码的生命周期内不会改变。 C# 编译器不会这样做。
JIT 编译器可能。没有理由不能。它拥有所有代码。内联属性getter是完全自由的,如果抖动确定内联的属性getter返回的值可以缓存在寄存器中并重新使用,那么就可以自由行了。 (如果你不希望它这样做,因为值可以在另一个线程上修改,那么你已经有一个竞争条件错误;在你担心性能之前修复这个错误。)
抖动是否实际上确实内联属性获取然后注册值,我不知道。我对抖动几乎一无所知。但如果它认为合适,它是被允许这样做的。如果您对它是否这样做感到好奇,您可以 (1) 询问编写抖动的团队中的某个人,或者 (2) 在调试器中检查抖动代码。
最后,让我借此机会指出,一次计算结果、存储结果并重新使用它并不总是一种优化。这是一个出乎意料的复杂问题。有各种各样的事情需要优化:
执行时间
可执行代码大小 -- 这对可执行时间有重大影响,因为大代码需要更长的加载时间、增加工作集大小、给处理器缓存、RAM 和页面文件带来压力。从长远来看,在启动时间和缓存位置等重要指标方面,小而慢的代码通常更快。
寄存器分配 -- 这对执行时间也有重大影响,尤其是在 x86 等具有少量可用寄存器的架构中。为快速重用注册一个值可能意味着可用于其他需要优化的操作的寄存器更少;也许优化这些操作将是一个净赢。
等等。它很快就会变得非常复杂。
简而言之,您不可能知道编写代码来缓存结果而不是重新计算结果实际上是 (1) 更快,还是 (2) 性能更好。 更好的性能并不总是意味着更快地执行特定例程。更好的性能是关于弄清楚哪些资源对用户很重要——执行时间、内存、工作集、启动时间等等- 并针对这些事情进行优化。除非 (1) 与您的客户交谈以了解他们关心什么,以及 (2) 进行实际测量以查看您的更改是否在期望的方向上产生了可衡量的效果,否则您无法做到这一点。
关于c# - C# 编译器是否足够智能来优化此代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47389525/