总是很难给出一个“更好”的解决方案(在哪些方面更好——代码行、可读性、执行速度、机器代码指令的字节数……?)但是因为你问的是执行速度这种情况下,我们可以重点说一下。
您可以引入您建议的变量,并在知道答案后使用它将条件简化为简单的小于条件。在大多数架构上,小于条件通常会转换为两条机器代码指令(例如,在 Intel IA-32 上, CMP
(比较)后跟JL
(如果小于则跳转)或JNL
(如果不小于则跳转))。运气好的话,编译器会注意到(或者你可以自己做,但我更喜欢到处都有相同模式所带来的清晰性),这在前两个语句中总是正确的,并对其进行trues < 2
优化。if()
int trues = 0;
if (trues < 2 && condition1) trues++;
if (trues < 2 && condition2) trues++;
if (trues < 2 && condition3) trues++;
// ...
if (trues >= 2)
{
// do something
}
conditionN
由于大多数语言的布尔短路行为,一旦知道答案,这会将可能的复杂评估减少为简单的小于比较。
如果您的语言允许您将布尔条件转换为整数,则另一个可能的变体是利用它来减少源代码行的数量。但是,您仍将评估每个条件。
if( (int)(condition1)
+ (int)(condition2)
+ (int)(condition3)
>= 2)
{
// do something
}
这是基于将布尔 FALSE 值转换为整数会导致 0,而将 TRUE 转换为 1 的假设。您也可以使用条件运算符来获得相同的效果,但请注意它可能会引入额外的分支。
if( ((condition1) ? 1 : 0)
+ ((condition2) ? 1 : 0)
+ ((condition3) ? 1 : 0)
>= 2)
{
// do something
}
根据编译器的优化器的智能程度,它可能能够确定一旦任何两个条件评估为真,整个条件将始终评估为真,并基于此进行优化。
- 请注意,除非您实际分析了您的代码并确定这是罪魁祸首,否则这可能是过早优化的情况。始终努力让代码首先被人类程序员阅读,然后让计算机快速执行,除非你能证明你正在查看的特定代码是一个实际的性能瓶颈。了解该分析器的工作原理并充分利用它。请记住,在大多数情况下,程序员的时间比 CPU 时间要昂贵得多,而聪明的技术需要更长的时间来让维护程序员解析。
- 此外,编译器是非常聪明的软件。有时他们实际上会检测到编写代码的意图,并能够使用旨在使这些操作更快的特定结构,但这取决于它能够确定您正在尝试做什么。一个完美的例子是使用中间变量交换两个变量,这在 IA-32 上可以通过
XCHG
消除中间变量来完成,但是编译器必须能够确定您实际上正在这样做,而不是聪明的事情可能会给在某些情况下的另一个结果。
- 由于绝大多数编写的非显式一次性软件在其生命周期中的绝大部分时间都处于维护模式(并且许多编写的一次性软件仍然存在并且远远超过其预期的最佳使用日期),因此优化可维护性是有意义的除非这在其他方面付出了不可接受的代价。当然,如果你在一个紧密的循环中评估这些条件一万亿次,那么有针对性的优化可能很有意义。但是分析器会从性能的角度准确地告诉您代码的哪些部分需要更仔细地检查,这意味着您可以避免不必要地使代码复杂化。
上面的警告说,我最近一直在研究代码,做出的修改乍一看几乎肯定会被认为是过早的细节优化。如果您需要高性能并使用分析器来确定代码的哪些部分是瓶颈,那么优化还不算早。(然而,根据具体情况,他们可能仍然是不明智的。)