【问题标题】:How to optimize a statement of the form "if (A == B) { ...} else if (A < B) {...} else { ....}" [closed]如何优化“if (A == B) { ...} else if (A < B) {...} else { ....}”形式的语句 [关闭]
【发布时间】:2014-05-28 17:39:27
【问题描述】:

我有一段类似的代码

if (A == B)
{
    ...
}
else if (A < B)
{
    ... 
}
else // (A > B)
{
    ...
}

我意识到存在冗余问题,因为在==&lt; 的计算中会有一些相同的位比较。如何优化我的代码以使其更美观、更快?

【问题讨论】:

  • 分支预测对运行速度的影响远大于最小化一次比较。
  • 除此之外 - 这确实是一个过早的优化,如果您有一个针对 &lt;,&gt;,== 具有 3 种不同行为的逻辑,您应该使用您描述的 3 个条件,使其可读。
  • 我认为您应该更改您的用户名。 Donald Knuth 会知道解决方案 :-)
  • 某些语言可能会提供three way comparison

标签: optimization premature-optimization


【解决方案1】:

您没有指定语言,但取决于语言,这可以通过多种方式重写

Ruby方式(使用spaceship operator):

case A <=> B
    when -1 then... # A < B
    when  0 then... # A = B
    when  1 then... # A > B
end

PerlPHP7 和 Groovy 也有相同的运算符。许多其他语言具有类似的运算符或函数,用于相同的组合比较目的,例如cmp in Python 2, compare in OCaml and compareTo in Kotlin。 C# 没有该运算符,但它具有 IComparable 接口和 CompareTo 方法。

VB方式:

Select Case A
    Case Is < B
        ...
    Case Is = B
        ...
    Case Is > B
        ...
End Select

在 C、C++ 和许多没有 CompareTo 方法的类 C 语言中,您可以使用这种方式

int cmp = (A > B) - (A < B);
switch (cmp)
{
    case -1: ...
    case  0: ...
    case  1; ...
}

许多语言(如 Java)不允许您直接将比较结果用作数值。在这种情况下,您可以使用signum function

switch(Integer.signum(A - B))

您可以在 C 和 C++ 中轻松实现signum 函数like this

这些是针对高级语言的。在装配级别,事情更简单。在 x86 汇编中只需要一个比较,然后根据结果我们将跳转到相应的块,所以它是 不是 3 比较和编译器足够聪明来优化这个简单的情况。例如:

    cmp eax, ebx
    je EQUAL_TO      ; jump if =
    ja GREATER_THAN  ; jump if >

    ; less than case's code
    jmp END_CMP

EQUAL_TO:
    ; equal case's code
    jmp END_CMP

GREATER_THAN:
    ; larger than case's code

END_CMP:

与其他具有比较标志(如 ARM 或 68k)的架构相同......

MIPS 示例:

    beq $t0, $t1, EQUAL_TO       # $t0 = A, $t1 = B; if ($t0 == $t1) equal();
    slt $t0, $t1, $t2            # $t2 = is_less_than = ($t0 < $t1);
    beq $t2, $zero, GREATER_THAN # if (!is_less_than) larger();

    # "less than" code here
    # ...
    j END_CMP

EQUAL_TO:
    # "equal" code
    # ...
    j END_CMP

GREATER_THAN:
    # "larger" code
    # ...

END_CMP:

对于具有条件指令的架构,例如 ARM 或 Itanium,并且 if-else 块中的主体足够简单您甚至可能不需要跳转

【讨论】:

  • 组装的好答案,尽管在测试了相等和以上/更大之后,剩下的所有内容都低于/更少,因此不需要自己进行测试 - 根据 OP 的示例。这就是 gcc 和其他编译器会做的事情。在调试生成的代码中,您可能会显式执行两个测试,比较和全部。通过首先测试最常见/时间关键的情况,然后转向越来越不常见/时间关键的变体,OP 的代码可能(在某种程度上)得到优化。
【解决方案2】:

对于 C#,您可以使用接受 2 个值的通用函数,然后为每种情况使用一个 lambda 操作。

void CompareAndAct<T>(T a, T b, Action fnEqualTo, Action fnLessThan, Action fnGreaterThan)  {

   var comparison = System.Collections.Generic.Comparer<T>.Default.Compare(a, b);
   if (comparison == 0) {
      fnEqualTo();
   }
   else if (comparison < 0) {
      fnLessThan();
   }
   else {  //A > B
      fnGreaterThan();
   }
}

然后你可以随意重复使用它:

CompareAndAct(a,b, () => Console.Writeline("Equal"),() => Console.WriteLine("Less Than", () => Console.WriteLine("Greater Than"));

我不能说我会推荐这样做,但它会起作用。它没有更快(可能更慢),但我想有人可以说它“更高级”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-02
    • 2023-03-28
    • 1970-01-01
    • 2020-11-23
    • 1970-01-01
    • 2015-07-28
    • 2014-08-12
    • 2018-01-29
    相关资源
    最近更新 更多