一尘不染

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

c#

请忽略此问题中的代码可读性。

在性能方面,应将以下代码编写为:

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 { return _maxResults; } }

C#编译器是否将其MaxResults视为黑匣子并每次对其进行评估?还是足够聪明地弄清楚我有3个调用相同属性的调用,而两次调用之间没有对该属性的修改?如果MaxResults是场怎么办?

优化的法则之一是预先计算,因此我本能地像第一个清单那样编写此代码,但是我很好奇这种事情是否是自动为我完成的(再次,请忽略代码的可读性)。

(注意:我对听到“微优化”的论点不感兴趣,该论点在我所发布的特定案例中可能是有效的。我只想了解正在发生或未发生的事情背后的理论。)


阅读 394

收藏
2020-05-19

共1个答案

一尘不染

首先,实际回答性能问题的唯一方法是实际尝试两种方法并在实际条件下测试结果。

也就是说,其他回答说“编译器”不执行此优化,因为该属性可能会产生副作用,这是对还是错。该问题的问题(除了根本的问题,即如果不实际尝试并测量结果就无法回答),实际上“编译器”是两个编译器:编译为MSIL的C#编译器和JIT编译器,将IL编译为机器代码。

C#编译器从不进行这种优化。如前所述,这样做将要求编译器查看正在调用的代码,并验证其计算的结果在被调用者代码的生存期内未发生变化。C#编译器不这样做。

JIT编译器可能会这样做。没有理由不能这样做。它拥有所有代码。内联属性获取器是完全免费的,如果抖动确定内联属性获取器返回的值可以缓存在寄存器中并重新使用,则可以这样做。(如果您不希望这样做,因为可以在另一个线程上修改该值,则说明您已经有一个竞争条件错误;请在担心性能之前修复该错误。)

抖动是否 确实
内联了属性获取并随后注册了值,我不知道。我对抖动几乎一无所知。但是,如果认为合适,则可以这样做。如果您对是否这样做感到好奇,则可以(1)询问编写抖动的团队中的某个人,或者(2)在调试器中检查抖动的代码。

最后,让我借此机会指出,一次计算结果,存储结果并重新使用它 并不总是一种优化 。这是一个令人惊讶的复杂问题。有许多要优化的东西:

  • 执行时间处理时间

  • 可执行代码的大小-这对可执行时间有重大影响,因为大代码的加载时间更长,工作集大小增加,对处理器缓存,RAM和页面文件施加压力。小慢的代码往往是在长期运行 速度 比类似的启动时间和缓存位置重要的指标大快代码。

  • 寄存器分配-这对执行时间也有重要影响,特别是在x86之类的架构中,可用寄存器数量很少。为快速重用而注册一个值可能意味着更少的寄存器可用于其他需要优化的操作。也许优化这些操作将是一个净赢。

  • 等等。它很快就会变得复杂。

简而言之,您可能无法知道编写代码来缓存结果而不是重新计算结果实际上是(1)更快,还是(2)性能更好。 更好的性能并不总是意味着更快地执行特定例程。
更好的性能在于确定哪些资源对用户很重要-执行时间,内存,工作集,启动时间等-
并对这些内容进行优化。没有(1)与客户交谈以找出他们关心的问题,以及(2)实际进行测量以查看您的更改是否在预期的方向上产生了可测量的效果,您就无法做到这一点。

2020-05-19