【问题标题】:How often do you worry about how many if cases will need to be processed?您多久担心需要处理多少个案?
【发布时间】:2023-04-11 07:32:01
【问题描述】:

如果您有以下情况:

$var = 3; // we'll say it's set to 3 for this example
if ($var == 4) {
    // do something
} else if ($var == 5) {
    // do something
} else if ($var == 2) {
    // do something
} else if ($var == 3) {
    // do something
} else {
    // do something
}

如果说 80% 的时间 $var 是 3,您是否担心在找到真实案例之前它会经历 4 个 if 案例?

我认为在一个小型网站上这没什么大不了的,但是如果该 if 语句每秒运行 1000 次呢?

我正在使用 PHP,但我认为语言无关紧要。

【问题讨论】:

    标签: language-agnostic optimization performance if-statement


    【解决方案1】:

    这是我过去为雷达系统编写软件时的做法。 (速度在雷达中很重要。这是少数几个“实时”实际上意味着“真实”而不是“快速”的地方之一。)

    [我将切换到 Python 语法,这对我来说更容易,我相信你能解释它。]

    if var <= 3:
        if var == 2:
            # do something
        elif var == 3:
            # do something
        else: 
            raise Exception
    else:
        if var == 4:
            # do something
        elif var == 5:
            # do something
        else:
            raise Exception
    

    您的 if 语句形成树而不是平面列表。当您向此列表添加条件时,您会在树的中心周围晃动。 n 次比较的扁平序列平均需要 n/2 步。该树导致进行 log(n) 比较的一系列比较。

    【讨论】:

    • 好主意。我知道我以前做过类似的事情,但也许我用得更频繁
    • 大声笑,将其更改为“var
    【解决方案2】:

    嗯,我相信几乎一直,例如,具有数字排序值的易读性会覆盖您通过减少比较指令的数量可能获得的任何微小好处。

    话虽如此,与所有优化一样:

    1. 让它发挥作用
    2. 测量它
    3. 如果速度够快,别管它
    4. 如果太慢,那么优化它

    哦,我可能会从一开始就使用开关/外壳! ;-)

    【讨论】:

    • 是的,你是对的,开关会更好,但我正在寻找更大范围的可能 if 语句
    【解决方案3】:

    发生这种情况的典型案例(在您的帖子中实际上有 5 个选项)是在 ffmpeg 中的 decode_cabac_residual 函数中。这是相当重要的,因为分析(非常重要——不要在分析之前优化!)表明它占 H.264 视频解码所花费时间的 10-15% 以上。 if 语句控制一组语句,这些语句对要解码的各种残差类型的计算方式不同——不幸的是,如果函数对 5 种类型中的每一种重复 5 次,则会由于代码大小而损失太多速度剩余的。因此,必须使用 if 链。

    对许多常见的测试流进行了分析,以根据可能性对它们进行排序;顶部是最常见的,底部是最少的。这会带来很小的速度增益。

    现在,在 PHP 中,我怀疑您在 C 中获得的低级样式速度增益要少得多,如上例所示。

    【讨论】:

      【解决方案4】:

      使用 switch/case 语句绝对是最好的方法。

      这使编译器(解释器)有机会利用跳转表到达正确的分支,而无需进行 N 次比较。想象一下它创建了一个索引为 0、1、2、.. 的地址数组,然后它可以通过一次操作在数组中查找正确的地址。

      另外,由于 case 语句中的语法开销较小,因此它也更容易阅读。

      更新:如果比较适用于 switch 语句,那么这就是配置文件引导优化可以提供帮助的领域。通过运行具有实际测试负载的 PGO 构建,系统可以生成分支使用信息,然后使用它来优化所采用的路径。

      【讨论】:

      • 一般来说我想的更多,但是,是的,如果它就像在示例中一样,我肯定会使用开关
      【解决方案5】:

      我不会回答 PHP 问题,而是更笼统地回答一下。它并不直接适用于 PHP,因为它会经过某种解释。

      如果需要,许多编译器可以在 if-elif-elif-... 块之间进行转换以切换块,并且 elif 部分中的测试足够简单(并且其余的语义恰好是兼容的)。对于 3-4 次测试,使用跳转表不一定有什么好处。

      原因是 CPU 中的分支预测器非常擅长预测发生的事情。实际上,唯一发生的事情是对指令获取的压力有点高,但它几乎不会是惊天动地的。

      但是,在您的示例中,大多数编译器会识别 $var 是常量 3,然后在 if..elif.. 块中将 $var 替换为 3。这反过来又使表达式保持不变,因此它们被折叠为真或假。所有的假分支都被死代码消除器杀死,并且对真的测试也被消除了。剩下的是 $var == 3 的情况。不过你不能依赖 PHP 这么聪明。一般来说,您无法进行 $var 的传播,但它可能可以从某些调用站点进行。

      【讨论】:

      • 这突出了使用示例而不是真实世界代码的问题。我敢肯定,从概念上讲,变量的恒定性不是原始“问题”的一部分;那太荒谬了。它肯定会部分来自用户输入,而不是完全“随机”。
      【解决方案6】:

      您可以尝试使用您调用的代码块数组。那么所有代码​​块的开销都是一样的。

      Perl 6:

      our @code_blocks = (
        { 'Code Block 0' },
        { 'Code Block 1' },
        { 'Code Block 2' },
        { 'Code Block 3' },
        { 'Code Block 4' },
        { 'Code Block 5' },
      );
      
      if( 0 <= $var < @code_blocks.length ){
        @code_blocks[$var]->();
      }
      

      【讨论】:

        【解决方案7】:

        如果代码必须进行额外的测试,那么它肯定会运行得更慢。如果在这部分代码中性能很关键,那么您应该将最常见的情况放在首位。

        当您不确定性能是否足够快时,我通常同意“测量,然后优化”方法,但如果代码只是需要尽可能快地运行并且修复就像重新排列一样简单测试,然后我现在会快速编写代码,并在您上线后进行一些测量,以确保您的假设(例如 3 将在 80% 的时间发生)实际上是正确的。

        【讨论】:

          【解决方案8】:

          对于纯粹是相等性分析的代码,我会将其移至 switch/case,因为这样可以提供更好的性能。

          $var = 3; // we'll say it's set to 3 for this example
          switch($var)
           {
             case 4:
                //do something
                break;
             case 5:
                //do something
                break;
             case:
                //do something when none of the provided cases match (same as using an else{ after the elseif{
           }
          

          现在,如果您进行更复杂的比较,我会将它们嵌套在 switch 中,或者只使用 elseif。

          【讨论】:

            【解决方案9】:

            在面向对象的语言中,如果一个选项提供了大量的 if,那么这意味着您应该将行为(例如,您的 //do something 块)移动到包含该值的对象。

            【讨论】:

              【解决方案10】:

              只有您可以判断优化顺序或将其重新排列为实际上是二叉树的性能差异是否会产生显着差异。但我怀疑你必须每秒有数百万次,而不是数千次,才能在 PHP 中思考它(在某些其他语言中更是如此)。

              计时。看看你每秒可以运行多少次 if/else if/else 语句而不执行任何操作并且 $var 不是选项之一。

              【讨论】:

                猜你喜欢
                • 2020-02-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-04-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多