问题描述
请忽略此问题中的代码可读性.
Please ignore code readability in this question.
在性能方面,下面的代码是否应该这样写:
In terms of performance, should the following code be written like this:
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
是一个class
,MaxResults
是一个简单的整数属性(即public int MaxResults { get {返回 _maxResults; } }
.
criteria
is a class
, and MaxResults
is a simple integer property (i.e., public int MaxResults { get { return _maxResults; } }
.
C# 编译器是否将 MaxResults
视为黑盒并每次都对其进行评估?或者是否足够聪明地发现我已经对同一个属性进行了 3 次调用,而在调用之间没有修改该属性?如果 MaxResults
是一个字段怎么办?
Does the C# compiler treat MaxResults
as a black box and evaluate it every time? Or is it smart enough to figure out that I've got 3 calls to the same property with no modification of that property between the calls? What if MaxResults
was a field?
优化法则之一是预先计算,所以我本能地像第一个清单一样编写了这段代码,但我很好奇这种事情是否会自动为我完成(再次,忽略代码可读性).
One of the laws of optimization is precalculation, so I instinctively wrote this code like the first listing, but I'm curious if this kind of thing is being done for me automatically (again, ignore code readability).
(注意:我对听到微优化"论点不感兴趣,这在我发布的特定案例中可能是有效的.我只是想了解一些关于发生或不发生的理论.)
(Note: I'm not interested in hearing the 'micro-optimization' argument, which may be valid in the specific case I've posted. I'd just like some theory behind what's going on or not going on.)
推荐答案
首先,真正回答性能问题的唯一方法是实际尝试两种方式并在现实条件下测试结果.
First off, the only way to actually answer performance questions is to actually try it both ways and test the results in realistic conditions.
也就是说,其他答案说编译器"没有进行此优化,因为该属性可能具有副作用,既对又错.问题的问题(除了根本问题,如果不实际尝试并测量结果就无法回答)是编译器"实际上是两个编译器:编译为 MSIL 的 C# 编译器和 JIT 编译器,它将 IL 编译为机器代码.
That said, the other answers which say that "the compiler" does not do this optimization because the property might have side effects are both right and wrong. The problem with the question (aside from the fundamental problem that it simply cannot be answered without actually trying it and measuring the result) is that "the compiler" is actually two compilers: the C# compiler, which compiles to MSIL, and the JIT compiler, which compiles IL to machine code.
C# 编译器从不做这种优化;如前所述,这样做需要编译器对等到被调用的代码,并验证它计算的结果在被调用者代码的生命周期内不会改变.C# 编译器不这样做.
The C# compiler never ever does this sort of optimization; as noted, doing so would require that the compiler peer into the code being called and verify that the result it computes does not change over the lifetime of the callee's code. The C# compiler does not do so.
JIT 编译器可能.没有理由不能.它有所有的代码.内联属性getter是完全自由的,如果抖动确定内联的属性getter返回一个可以缓存在寄存器中并重复使用的值,那么这样做是自由的.(如果您不希望它这样做,因为可以在另一个线程上修改该值,那么您已经有一个竞争条件错误;请在担心性能之前修复该错误.)
The JIT compiler might. No reason why it couldn't. It has all the code sitting right there. It is completely free to inline the property getter, and if the jitter determines that the inlined property getter returns a value that can be cached in a register and re-used, then it is free to do so. (If you don't want it to do so because the value could be modified on another thread then you already have a race condition bug; fix the bug before you worry about performance.)
抖动是否真的 内联属性获取然后注册值,我不知道.我对抖动几乎一无所知.但如果它认为合适,它是允许这样做的.如果您对它是否这样做感到好奇,您可以 (1) 询问编写 jitter 的团队中的某个人,或 (2) 在调试器中检查 jitter 代码.
Whether the jitter actually does inline the property fetch and then enregister the value, I have no idea. I know practically nothing about the jitter. But it is allowed to do so if it sees fit. If you are curious about whether it does so or not, you can either (1) ask someone who is on the team that wrote the jitter, or (2) examine the jitted code in the debugger.
最后,让我借此机会指出一次计算结果、存储结果并重新使用它并不总是一种优化.这是一个令人惊讶的复杂问题.有各种各样的事情需要优化:
And finally, let me take this opportunity to note that computing results once, storing the result and re-using it is not always an optimization. This is a surprisingly complicated question. There are all kinds of things to optimize for:
执行时间
execution time
可执行代码大小——这对可执行时间有很大影响,因为大代码需要更长的加载时间,增加工作集大小,对处理器缓存、RAM 和页面文件造成压力.从长远来看,在启动时间和缓存位置等重要指标上,小慢代码通常快快于大快代码.
executable code size -- this has a major effect on executable time because big code takes longer to load, increases the working set size, puts pressure on processor caches, RAM and the page file. Small slow code is often in the long run faster than big fast code in important metrics like startup time and cache locality.
寄存器分配——这对执行时间也有很大影响,特别是在像 x86 这样只有少量可用寄存器的架构中.为快速重用注册一个值可能意味着可用于其他需要优化的操作的寄存器更少;也许优化这些操作会是一个净赢.
register allocation -- this also has a major effect on execution time, particularly in architectures like x86 which have a small number of available registers. Enregistering a value for fast re-use can mean that there are fewer registers available for other operations that need optimization; perhaps optimizing those operations instead would be a net win.
等等.它变得非常复杂非常快.
and so on. It get real complicated real fast.
简而言之,您不可能知道编写代码来缓存结果而不是重新计算结果实际上是 (1) 更快,还是 (2) 性能更好.更好的性能并不总是意味着更快地执行特定的例程. 更好的性能是要弄清楚哪些资源对用户很重要——执行时间、内存、工作集、启动时间等等- 并针对这些事情进行优化.如果不 (1) 与客户交谈以了解他们关心的内容,以及 (2) 实际衡量以查看您的更改是否对预期方向产生可衡量的影响,您就无法做到这一点.
In short, you cannot possibly know whether writing the code to cache the result rather than recomputing it is actually (1) faster, or (2) better performing. Better performance does not always mean making execution of a particular routine faster. Better performance is about figuring out what resources are important to the user -- execution time, memory, working set, startup time, and so on -- and optimizing for those things. You cannot do that without (1) talking to your customers to find out what they care about, and (2) actually measuring to see if your changes are having a measurable effect in the desired direction.
这篇关于C# 编译器是否足够智能以优化此代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!