2016-09-15 51 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的艱苦的過程。基於源代碼單獨是基於一般的想法,可能的改進的分析,常應用於

優化技術,而不需要通過代碼部分是否將要被但內心也認識到代碼頻繁執行的擔心循環語句值得額外關注。

相關問題