【问题标题】:If vs. Switch Speed如果与开关速度
【发布时间】:2010-10-01 11:57:30
【问题描述】:

由于编译器优化,Switch 语句通常比等效的 if-else-if 语句(例如,在此 article 中描述的)更快。

这种优化实际上是如何工作的?谁有好的解释?

【问题讨论】:

标签: c# performance switch-statement if-statement


【解决方案1】:

编译器可以在适用的情况下构建跳转表。例如,当您使用反射器查看生成的代码时,您会看到对于字符串上的巨大开关,编译器实际上会生成使用哈希表来调度这些的代码。哈希表使用字符串作为键,并将case 代码作为值。

与许多链接的if 测试相比,这具有更好的运行时间,并且实际上即使对于相对较少的字符串也更快。

【讨论】:

  • 在某些情况下它们还会转换为树比较。推理有点复杂,但基本上归结为表间接中性现代 cpu 跳转目标缓冲区,因此消除了分支预测器。我隐约记得在 GCC 会议上关于开关的 codegen 的一篇论文。
  • 这意味着: switch (a) case "x": case "y": case "z": //something break; } 比: if(a=="x"||a=="b"||a=="c") //something right?
  • 这里我们没有嵌套 if else,只有 OR 那么你怎么看?
  • @yazanpro 在旧的编译器上可能是的(但请注意,案例的数量是如此之少,以至于它可能不会产生影响!)。但是,现代编译器会进行更多的代码分析。因此,他们可能会发现这两个代码 sn-ps 是等效的,并应用相同的优化。但这纯粹是我的猜测,我不知道是否有任何编译器真的这样做。
【解决方案2】:

这是一个轻微的简化,因为通常任何现代编译器都会遇到if..else if .. 序列,该序列可以被人轻松转换为 switch 语句,编译器也会这样做。但只是为了增加额外的乐趣,编译器不受语法限制,因此可以在内部生成类似“switch”的语句,其中包含范围、单个目标等的混合——并且他们可以(并且可以)为 switch 和 if 执行此操作。 .else 语句。

Anyhoo,Konrad 回答的一个扩展是编译器可能会生成一个跳转表,但这不一定能保证(也不可取)。由于多种原因,跳转表对现代处理器上的分支预测器有不良影响,而表本身对缓存行为也有不良影响,例如。

switch(a) { case 0: ...; break; case 1: ...; break; }

如果编译器实际上为此生成了一个跳转表,则可能会比替代的if..else if.. 样式代码慢,因为跳转表会破坏分支预测。

【讨论】:

    【解决方案3】:

    不匹配的统计数据可能不好。

    如果您实际下载源代码,则在 if 和 switch 情况下,不匹配值已知为 21。编译器应该能够抽象出来,知道应该始终运行哪个语句,并且 CPU 应该能够正确地进行分支预测。

    在我看来,更有趣的情况是并非所有情况都失败,但这可能不是实验的范围。

    【讨论】:

      【解决方案4】:

      switch/case 语句通常可能更快 1 级深度,但是当您开始进入 2 级或更多时,switch/case 语句开始花费的时间是嵌套 if/else 语句的 2-3 倍。

      This article has some speed comparisons 突出显示嵌套此类语句时的速度差异。

      例如,根据他们的测试,示例代码如下:

      if (x % 3 == 0)
                  if (y % 3 == 0)
                      total += 3;
                  else if (y % 3 == 1)
                      total += 2;
                  else if (y % 3 == 2)
                      total += 1;
                  else
                      total += 0;
              else if (x % 3 == 1)
                  if (y % 3 == 0)
                      total += 3;
                  else if (y % 3 == 1)
                      total += 2;
                  else if (y % 3 == 2)
                      total += 1;
                  else
                      total += 0;
              else if (x % 3 == 2)
                  if (y % 3 == 0)
                      total += 3;
                  else if (y % 3 == 1)
                      total += 2;
                  else if (y % 3 == 2)
                      total += 1;
                  else
                      total += 0;
              else
                  if (y % 3 == 0)
                      total += 3;
                  else if (y % 3 == 1)
                      total += 2;
                  else if (y % 3 == 2)
                      total += 1;
                  else
                      total += 0;
      

      在等效的 switch/case 语句运行时间的 一半 内完成:

      switch (x % 3)
          {
              case 0:
                  switch (y % 3)
                  {
                      case 0: total += 3;
                          break;
                      case 1: total += 2;
                          break;
                      case 2: total += 1;
                          break;
                      default: total += 0;
                          break;
                  }
                  break;
              case 1:
                  switch (y % 3)
                  {
                      case 0: total += 3;
                          break;
                      case 1: total += 2;
                          break;
                      case 2: total += 1;
                          break;
                      default: total += 0;
                          break;
                  }
                  break;
          case 2:
                  switch (y % 3)
                  {
                      case 0: total += 3;
                          break;
                      case 1: total += 2;
                          break;
                      case 2: total += 1;
                          break;
                      default: total += 0;
                          break;
                  }
                  break;
          default:
              switch (y % 3)
              {
                  case 0: total += 3;
                      break;
                  case 1: total += 2;
                      break;
                  case 2: total += 1;
                      break;
                  default: total += 0;
                      break;
              }
              break;
          }
      

      是的,这是一个基本示例,但它说明了这一点。

      所以结论可能是对只有一层深的简单类型使用 switch/case,但对于更复杂的比较和多个嵌套级别,使用经典的 if/else 构造?

      【讨论】:

      • -1: 1. 这篇文章完全忽略了分支预测,2. 算法并不完全相同(链接上的单个 if-else 已经编码更优化)和 3.发现的差异是如此之小,以至于没有任何借口可以使用正确、干净的代码(在 switch 和相同的 if-else 构造之间的 10.000.000 次调用中大约需要 4 ns)
      • 该示例不会被优化,因为 switch 块的情况很少。通常在 5-6 个元素之后它会生成一个跳转表。
      【解决方案5】:

      if over 情况的唯一优点是当第一种情况的出现频率显着增加时。

      不确定阈值的确切位置,但我使用 case 语法,除非第一个“几乎总是”通过第一个测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-30
        • 2011-03-24
        • 2013-07-20
        • 1970-01-01
        • 1970-01-01
        • 2011-09-05
        • 2019-06-13
        相关资源
        最近更新 更多