简体   繁体   English

C#编译器是否足够聪明以优化此代码?

[英]Is the C# compiler smart enough to optimize this code?

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

or like this: 或像这样:

if (criteria.MaxResults > 0)
{
    while (accounts.Count > criteria.MaxResults)
        accounts.RemoveAt(criteria.MaxResults);
}

?

Edit: criteria is a class , and MaxResults is a simple integer property (ie, public int MaxResults { get { return _maxResults; } } . 编辑: criteria是一个classMaxResults是一个简单的整数属性(即public int MaxResults { get { return _maxResults; } }

Does the C# compiler treat MaxResults as a black box and evaluate it every time? C#编译器是否将MaxResults视为黑盒并每次对其进行评估? 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? 还是足够聪明地弄清楚我有3个调用相同属性的调用,而两次调用之间没有对该属性的修改? What if MaxResults was a field? 如果MaxResults是一个字段怎么办?

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. 首先,实际回答性能问题的唯一方法是实际尝试两种方法并在实际条件下测试结果。

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. 该问题的问题(除了根本的问题,即如果不实际尝试并测量结果就无法回答),实际上“编译器”是两个编译器:编译为MSIL的C#编译器和JIT编译器,将IL编译为机器代码。

The C# compiler never ever does this sort of optimization; C#编译器从不进行这种优化。 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. C#编译器不这样做。

The JIT compiler might. JIT编译器可能会这样做。 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.) (如果您不希望这样做,因为可以在另一个线程上修改该值,则说明您已经有一个竞争条件错误;请在担心性能之前修复该错误。)

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. 如果您对是否这样做感到好奇,则可以(1)询问编写抖动的团队中的某个人,或者(2)在调试器中检查抖动的代码。

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 执行时间处理时间

  • 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. 可执行代码的大小-这对可执行时间有重大影响,因为大代码的加载时间更长,工作集大小增加,对处理器缓存,RAM和页面文件施加压力。 Small slow code is often in the long run faster than big fast code in important metrics like startup time and cache locality. 在启动时间和缓存局部性等重要指标上,小而慢的代码从长远来看通常比大而快速的代码更快

  • register allocation -- this also has a major effect on execution time, particularly in architectures like x86 which have a small number of available registers. 寄存器分配-这对执行时间也有重要影响,特别是在x86之类的架构中,可用寄存器数量很少。 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. 它很快就会变得复杂。

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. 简而言之,您可能无法知道编写代码来缓存结果而不是重新计算结果实际上是(1)更快,还是(2)性能更好。 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. 没有(1)与客户交谈以找出他们关心的问题,以及(2)实际进行测量以查看您的更改是否在预期的方向上产生了可测量的效果,您就无法做到这一点。

If MaxResults is a property then no, it will not optimize it, because the getter may have complex logic, say: 如果MaxResults是一个属性,则为no,它将不会对其进行优化,因为getter可能具有复杂的逻辑,例如:

private int _maxResults;
public int MaxReuslts {
  get { return _maxResults++; }
  set { _maxResults = value; }
}

See how the behavior would change if it in-lines your code? 看看如果内联代码会改变行为?

If there's no logic...either method you wrote is fine, it's a very minute difference and all about how readable it is TO YOU (or your team) ...you're the one looking at it. 如果没有逻辑...您编写的任何一种方法都很好,那将是非常微小的差异,而且关于您(或您的团队)的可读性...都是您的选择。

Your two code samples are only guaranteed to have the same result in single-threaded environments, which .Net isn't, and if MaxResults is a field (not a property). 仅保证您的两个代码示例在单线程环境中具有相同的结果,而.Net并非如此,并且MaxResults是一个字段(而不是属性)。 The compiler can't assume, unless you use the synchronization features, that criteria.MaxResults won't change during the course of your loop. 除非您使用同步功能,否则编译器无法假定该criteria.MaxResults .MaxResults在循环过程中不会更改。 If it's a property, it can't assume that using the property doesn't have side effects. 如果它是一个属性,则不能假设使用该属性没有副作用。

Eric Lippert points out quite correctly that it depends a lot on what you mean by "the compiler". 埃里克·利珀特(Eric Lippert)相当正确地指出,它很大程度上取决于您所说的“编译器”的含义。 The C# -> IL compiler? C#-> IL编译器? Or the IL -> machine code (JIT) compiler? 还是IL->机器代码(JIT)编译器? And he's right to point out that the JIT may well be able to optimize the property getter, since it has all of the information (whereas the C# -> IL compiler doesn't, necessarily). 他正确地指出,JIT可能能够优化属性获取器,因为它具有所有信息(而C#-> IL编译器则不一定)。 It won't change the situation with multiple threads, but it's a good point nonetheless. 它不会改变具有多个线程的情况,但这仍然是一个好主意。

It will be called and evaluated every time. 每次都会调用并评估它。 The compiler has no way of determining if a method (or getter) is deterministic and pure (no side effects). 编译器无法确定方法(或getter)是否是确定性的和纯净的(无副作用)。

Note that actual evaluation of the property may be inlined by the JIT compiler, making it effectively as fast as a simple field. 请注意,JIT编译器可以内联该属性的实际评估,从而使其与简单字段一样有效。

It's good practise to make property evaluation an inexpensive operation. 优良作法是使属性评估成为廉价操作。 If you do some heavy calculation in the getter, consider caching the result manually, or changing it to a method. 如果在getter中进行大量计算,请考虑手动缓存结果,或将其更改为方法。

why not test it? 为什么不测试呢?

just set up 2 console apps make it look 10 million times and compare the results ... remember to run them as properly released apps that have been installed properly or else you cannot gurantee that you are not just running the msil. 只需设置2个控制台应用程序,使其看起来达到1000万次并比较结果...请记住将它们作为已正确安装的正确发布的应用程序运行,否则您将无法保证自己不仅在运行msil。

Really you are probably going to get about 5 answers saying 'you shouldn't worry about optimisation'. 确实,您可能会得到大约5个答案,说“您不应该担心优化”。 they clearly do not write routines that need to be as fast as possible before being readable (eg games). 他们显然并没有编写在可读之前需要尽可能快的例程(例如游戏)。

If this piece of code is part of a loop that is executed billions of times then this optimisation could be worthwhile. 如果这段代码是执行数十亿次循环的一部分,那么这种优化可能是值得的。 For instance max results could be an overridden method and so you may need to discuss virtual method calls. 例如,最大结果可能是覆盖的方法,因此您可能需要讨论虚拟方法调用。

Really the ONLY way to answer any of these questions is to figure out is this is a piece of code that will benefit from optimisation. 真正回答这些问题的唯一方法就是弄清楚这是一段将从优化中受益的代码。 Then you need to know the kinds of things that are increasing the time to execute. 然后,您需要了解增加执行时间的各种事情。 Really us mere mortals cannot do this a priori and so have to simply try 2-3 different versions of the code and then test it. 真的,我们仅仅是凡人不能先验地做到这一点,因此必须简单地尝试2-3个不同版本的代码,然后对其进行测试。

If criteria is a class type, I doubt it would be optimized, because another thread could always change that value in the meantime. 如果criteria是类类型,我怀疑它会被优化,因为与此同时另一个线程总是可以更改该值。 For struct s I'm not sure, but my gut feeling is that it won't be optimized, but I think it wouldn't make much difference in performance in that case anyhow. 对于struct我不确定,但是我的直觉是不会对其进行优化,但是我认为无论如何在性能上都不会有太大的不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM