冗余/性能更好的代码与优化/性能更低的代码

本文关键字:代码 性能 优化 冗余 更好 | 更新日期: 2023-09-27 18:31:02

就我而言,我使用的是C#,但这个问题的概念也适用于Java。希望答案足够通用,可以涵盖两种语言。否则最好将问题一分为二。

我一直在想哪一个是更好的做法。

编译器是否负责增强"第二"代码,使其性能与"第一"代码一样好?

是否可以同时获得"更好的性能""优化"的代码?

冗余/更好的性能代码:

string name = GetName(); // returned string could be empty
List<string> myListOfStrings = GetListOfStrings();
if(string.IsNullOrWhiteSpace(name)
{
    foreach(string s in myListOfStrings)
        Console.WriteLine(s);
}
else
{
    foreach(string s in myListOfStrings)
        Console.WriteLine(s + " (Name is: " + name);
}

优化/更少性能代码:

string name = GetName(); // returned string could be empty
List<string> myListOfStrings = GetListOfStrings();
foreach(string s in myListOfStrings)
    Console.WriteLine(string.IsNullOrWhiteSpace(name) ? s : s + " (Name is: " + name);

显然,"第一个"代码的执行时间更短,因为它执行条件"字符串"。IsNullOrWhiteSpace(name)' 每个循环只有一次。而"第二个"代码(更好)在每次迭代时执行条件。

请考虑长循环执行时间

而不是短循环执行时间,因为我知道当它很短时,性能不会有所不同。

冗余/性能更好的代码与优化/性能更低的代码

编译器是否负责增强"第二"代码,使其性能与"第一"代码一样好?

不,它不能。

  1. 它不知道布尔表达式在循环迭代之间不会更改。 代码可能不是每次都返回相同的值,因此强制在每次迭代中执行检查。

  2. 布尔表达式也可能产生副作用。 在这种情况下,它不会,但编译器无法知道这一点。 为了满足规范,执行此类副作用非常重要,因此需要在每次迭代中执行检查。

因此,您需要问的下一个问题是,在这种情况下,执行您提到的优化是否重要? 在任何情况下,我都可以想象您显示的确切代码,可能不是。 检查会非常快,几乎可以肯定不会成为瓶颈。 如果存在性能问题,几乎可以肯定有更大的鱼。

也就是说,只需对示例进行一些更改,就可以使其变得重要。 如果布尔表达式本身的计算成本很高(即它是数据库调用、Web 服务调用、一些昂贵的 CPU 计算等的结果),那么它可能是一个重要的性能优化。 另一种需要考虑的情况是,如果布尔表达式有副作用会发生什么。 如果是IEnumerator上的MoveNext电话怎么办? 如果重要的是它只执行一次,因为你不希望副作用发生N次,那么这就是一个非常重要的问题。

在这种情况下,有几种可能的解决方案。

最简单的是最有可能只计算一次布尔表达式,然后将其存储在变量中:

bool someValue = ComputeComplexBooleanValue();
foreach(var item in collection)
{
    if(someValue)
        doStuff(item);
    else
        doOtherStuff(item);
}

如果你想执行布尔值 0-1 次(即在集合为空的情况下避免调用它一次),那么我们可以使用 Lazy 来延迟计算该值,但要确保它仍然最多只计算一次:

var someValue = new Lazy<bool>(() => ComputeComplexBooleanValue());
foreach (var item in collection)
{
    if (someValue.Value)
        doStuff(item);
    else
        doOtherStuff(item);
}

您应该始终首先采用更容易理解和维护的方式。这意味着将重复代码减少到绝对最小值 (DRY)。此外,这种微优化对于许多系统来说并不那么重要。另请注意,较短的代码并不总是更好。

我想我会像这样:

string name = GetName(); // returned string could be empty
bool nameIsEmpty = string.IsNullOrWhiteSpace(name);
foreach (string s in GetListOfStrings()) {       
    string messageAddition = "";
    if (!nameIsEmpty) {
        messageAddition = " (Name is: " + name + ")";
    }
    Console.WriteLine(s + messageAddition);
    // more code which uses the computed value.. 
    // otherwise the condition can be moved out the loop
}

我发现一个额外的if语句比方法调用中的?:运算符更容易阅读,但这可能是个人喜好。

如果以后想要提高性能,则应分析应用程序并首先开始优化最慢的代码部分。也许您的GetListOfStrings()方法太慢了,以至于其他代码的性能完全无关紧要。如果您测量到复制循环可以显著提高性能,则可以考虑更改它。