0

就我而言,我使用的是 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);

显然,“第一个”代码的执行时间更少,因为它在每个循环中只执行一次条件“string.IsNullOrWhiteSpace(name)”。而“第二”代码(更好)在每次迭代时执行条件。

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

4

2 回答 2

3

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

不,它不能。

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

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

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

也就是说,只需对示例进行一些更改,就可以让它变得重要。如果布尔表达式本身计算量很大(即它是数据库调用、Web 服务调用、一些昂贵的 CPU 计算等的结果),那么它可能是重要的性能优化。另一个需要考虑的情况是如果布尔表达式有副作用会发生什么。如果它是一个MoveNext电话IEnumerator怎么办?如果它只执行一次很重要,因为您不希望副作用发生 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);
}
于 2013-09-12T18:27:30.483 回答
1

您应该始终先走更容易理解和维护的方式。这意味着将重复代码减少到绝对最小值(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()方法太慢了,以至于其他代码的性能完全无关紧要。如果您测量到复制循环可以显着提高性能,您可以考虑更改它。

于 2013-09-12T17:48:15.823 回答