冗余/性能更好的代码与优化/性能更低的代码
本文关键字:代码 性能 优化 冗余 更好 | 更新日期: 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)' 每个循环只有一次。而"第二个"代码(更好)在每次迭代时执行条件。
请考虑长循环执行时间而不是短循环执行时间,因为我知道当它很短时,性能不会有所不同。
编译器是否负责增强"第二"代码,使其性能与"第一"代码一样好?
不,它不能。
-
它不知道布尔表达式在循环迭代之间不会更改。 代码可能不是每次都返回相同的值,因此强制在每次迭代中执行检查。
-
布尔表达式也可能产生副作用。 在这种情况下,它不会,但编译器无法知道这一点。 为了满足规范,执行此类副作用非常重要,因此需要在每次迭代中执行检查。
因此,您需要问的下一个问题是,在这种情况下,执行您提到的优化是否重要? 在任何情况下,我都可以想象您显示的确切代码,可能不是。 检查会非常快,几乎可以肯定不会成为瓶颈。 如果存在性能问题,几乎可以肯定有更大的鱼。
也就是说,只需对示例进行一些更改,就可以使其变得重要。 如果布尔表达式本身的计算成本很高(即它是数据库调用、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()
方法太慢了,以至于其他代码的性能完全无关紧要。如果您测量到复制循环可以显著提高性能,则可以考虑更改它。