2016-09-15 74 views
2

我有一个简单的方法:为什么无用的IF子句会提高性能?

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子句会提高性能?


UPDATE

只是拍了一下到Webgrind毫秒报道:

前:Webgrind(毫秒),与if(! empty(...))

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

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

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

所以,以毫秒为单位消除不必要IF条款的统计增加了性能......如何的结果百分之结果以毫秒为单位的不同?

+9

您是如何设法评估性能的? –

+0

@Zeratops是正确的。 –

+0

Xdebug Profiler + Webgrind。只是添加了这两种情况下的屏幕截图。 – automatix

回答

-4

的,如果从左至右(& &)声明将进行评估,我的猜测是在某些情况下,空()函数返回一个真实结果这实际上意味着对strpos()函数将不会被调用。在这种情况下,如果要删除empty()函数调用,将始终评估strpos(),这可以解释性能损失(应忽略不计)。

+3

你混淆了&&'和'||'。 – Siguza

+1

正如我已经说过,我已经检查了这个:这个'IF'条款从来没有成功。 – automatix

0

正如在评论中指出的那样,这种变化和结果是如此之小以至于毫无意义。

在70k调用中,你只能获得171ms的差异,所以这可能是一些环境变化的结果,而不是你的代码。如果您再次运行此基准100次,则每次运行的差异都会发生变化(甚至可能会增加),作为对环境变化的响应。作为“环境”,我的意思是运行benckmark的计算机,数据传递的网络等。计算机运行的其他进程不是你编写的代码,所以如果另一个进程吃了某些CPU,代码运行缓慢比以前,等等。

要正确测试代码的基准,你需要考虑很多东西,而不仅仅是代码,甚至代码可以通过编译器等方式欺骗你(例如看看this video。这是JS基准测试,但很多的信息也可以应用于其他编程语言)。

2

实际上,为什么一个函数的证明速度比另一个函数稍微快一点,而且与您删除或添加empty()条件的事实无关。

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

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

首先看看this 3v4l,我们有和没有empty()条件分别validateStringByPrefix1()validateStringByPrefix2(),定义功能。在这里,我们首先请求validateStringByPrefix2(),这导致看起来是40 microsecond执行时间。请注意,在这两种情况下,函数都应返回false,并且empty($prefix)永远不会成立(就像您在自己的测试中所做的那样)。在第二个测试使用empty($prefix)它看起来实际上执行功能更快在11 microseconds

其次,看看this 3v4l,我们定义了相同的功能完全相同,但在validateStringByPrefix1()第一打电话,并得到相反的结果。现在它看起来像没有使用empty($prefix)该函数在12 microseconds处运行稍快,而另一个在88 microseconds处稍微慢一些。

请记住,microtime()实际上并不是一个准确的时钟。它可以在几微秒内轻微波动,但通常不足以在平均速度上慢一个数量级或更快。所以,是的,有一个区别,但没有这不是因为使用empty()或其缺乏。

相反,这个问题与典型的x86架构如何工作以及您的CPU和内存如何处理缓存有很多关系。您的代码中定义的函数通常会按照PHP首次执行的顺序(编译步骤发生在)存储在内存中。第一个被执行的函数将首先被缓存。有直写高速缓存的概念,例如,写分配无写分配可以影响这一点。下一个要执行的函数会覆盖该缓存,导致内存中的缓慢下降,这可能会或可能不会一致,这取决于我不会在这里介绍的因素。

然而,尽管所有这些微小的差异实在是没有,尽管在此代码使用或拆除empty()更快或更慢的结果。这些差异只是内存访问和每个程序都受到的分配折衷。

这就是为什么当你真的需要为以最快的速度执行微优化程序代码,您通常要经过做Profile-guided OptimizationPGO的艰苦的过程。基于源代码单独是基于一般的想法,可能的改进的分析,常应用于

优化技术,而不需要通过代码部分是否将要被但内心也认识到代码频繁执行的担心循环语句值得额外关注。

相关问题