为什么不必要的IF子句会提高性能?

时间:2016-09-15 12:24:33

标签: php performance is-empty

我有一个简单的方法:

public function validateStringByPrefix(string $string, $prefix)
{
    $valid = false;
    if (is_string($prefix)) {
        if (! empty($prefix) && strpos($string, $prefix) === 0) {
            $valid = true;
        }
    } elseif (is_array($prefix)) {
        foreach ($prefix as $partPrefix) {
            if (! empty($partPrefix) && strpos($string, $partPrefix) === 0) {
                $valid = true;
                break;
            }
        }
    }
    return $valid;
}

我注意到,条件! empty($prefix)实际上是不必要的,我删除了它。我预计性能的最小化或至少与改变之前的性能相同。但相反,表现变得更糟。

如果实际上有空$prefix$partPrefix的情况,那么它才有意义。由于empty(...)支票非常便宜。

但是没有这种情况,我已经检查了这个:

if(empty($prefix)) {
    die(__FILE__);
}

之前:Webgrind(百分比),if(! empty(...))

before (with if(! empty(...)))

之后:Webgrind(百分比),没有if(! empty(...))

after (without if(! empty(...)))

那么什么可以解释这种行为?为什么总是失败的不必要的IF子句会提高性能?

更新

以毫秒为单位查看Webgrind报告:

之前:Webgrind(以毫秒为单位),if(! empty(...))

before (in milliseconds, with if(! empty(...)))

之后:Webgrind(以毫秒为单位),没有if(! empty(...))

after (in milliseconds, without if(! empty(...)))

因此,以毫秒计算,删除不必要的IF子句可以提高性能...结果百分比与结果的差异如何?

2 个答案:

答案 0 :(得分:2)

实际上有一个完全合乎逻辑的理由,为什么一个函数比另一个函数证明更快,而且与删除或添加empty()条件的事实无关。

这些差异很小,必须以微秒为单位进行测量,但实际上存在差异。不同之处在于调用函数的顺序,或者更具体地说,它们在PHP VM的堆中分配的位置。

您在这里遇到的性能差异实际上可能因系统而异。所以不应该期望相同的结果。但是,基于有限数量的系统规格,存在一些期望。由此我们可以实际重现一致的结果,以证明为什么两个函数之间只有几微秒的差异。

首先看看this 3v4l,我们分别在empty()validateStringByPrefix1()条件下定义您的函数。在这里,我们首先调用validateStringByPrefix2(),这会导致执行时间validateStringByPrefix2()。注意,在这两种情况下,函数都应该返回false,并且40 microsecond将永远不会为真(正如您在自己的测试中所做的那样)。在第二次测试 使用empty($prefix)时,看起来函数在empty($prefix)实际执行得更快。

其次,请查看我们定义相同功能的this 3v4l,但先调用11 microseconds ,然后得到相反的结果。现在它看起来像没有使用validateStringByPrefix1()该函数在empty($prefix)运行得稍快,而另一个在12 microseconds运行稍慢。

请记住,88 microseconds实际上并不是一个准确的时钟。它可以在几微秒内稍微波动,但通常不足以平均更慢或更快一个数量级。所以,是的,存在差异,但没有它不是因为microtime()的使用或缺乏它。

相反,这个问题与典型的x86架构如何工作以及CPU和内存如何处理缓存有很大关系。代码中定义的函数通常由PHP按照它们首次执行的顺序存储到内存中(编译步骤在此处)。要执行的第一个函数将首先被缓存。例如,存在直写高速缓存的概念,其中写入分配无写入分配可以实现此目的。下一个要执行的函数会覆盖该缓存,导致内存缓慢减慢,这可能会也可能不一致,具体取决于我不会进入此处的因素。

然而,尽管所有这些微小的差异,尽管在此代码中使用或删除了empty() ,但确实没有更快或更慢的结果。这些差异只是每个程序遭受的内存访问和分配权衡。

这就是为什么当你真的需要微量优化程序代码以实现最快的执行时,你通常会经历艰难的过程Profile-guided Optimization PGO

  

仅基于源代码分析的优化技术基于关于可能改进的一般想法,通常应用而不用担心代码部分是否将被频繁执行,同时还认识到循环语句中的代码是值得格外注意。

答案 1 :(得分:0)

正如评论中所指出的,这种变化和结果非常小,没有任何意义。

在70k调用中,你只有171ms的差异,所以这可能是由于某些环境变化而不是你的代码。如果您再次运行此基准测试100次,则每次运行时差异都会发生变化(甚至可能会增加),作为对环境变化的响应。

As"环境"我的意思是运行benckmark的计算机,数据传递的网络等等。计算机正在运行除代码之外的其他进程,因此如果另一个进程占用某些CPU,则代码运行速度比以前慢,依此类推。 / p>

要正确地对代码进行基准测试,您需要考虑的不仅仅是代码,甚至代码也可以通过编译器等方式欺骗您(例如,请查看this video。它&#39 ; s JS基准测试,但很多信息也可以应用于其他编程语言。)