PHP的性能一直在提升。然而,如果用的不恰當,或是一個不留神,仍是可能會踩到PHP內部實現方面的坑的。我在前幾天的一個性能問題上就碰到了php
PHP的性能一直在提升。然而,如果用的不恰當,或是一個不留神,仍是可能會踩到PHP內部實現方面的坑的。我在前幾天的一個性能問題上就碰到了。
事 情是這樣子的,一位同事反饋咱們的一個接口每次返回須要5秒之久,咱們一塊兒review了代碼,「驚喜」的發現竟然在循環(大約900次)中調用了一個讀 緩存的操做,而這個緩存的key並無改變,所以咱們把這段代碼移到了循環外面,再測,接口返回時間降到了2秒,嗚呼!雖然提高了1倍,但明顯不是咱們能 接受的結果!
出現性能問題的代碼量並不大,咱們排除了IO問題之後,寫了一段測試代碼,果真問題很快重現。
html
複製代碼 代碼以下:node
<?php
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
$x[]= "{$j}";
}
for($i=0;$i<3000;$i++){
if(in_array($y,$x)){
continue;
}
}
?>
linux
shell$ time /usr/local/php/bin/php test.php
real 0m1.132s
user 0m1.118s
sys 0m0.015s
對的,咱們用的就是字符串型的數字,從緩存拿出來就是這樣子的啦!因此這裏是特地轉成字符串的(若是直接是數字,並不會出現這個問題 ,各位能夠自行驗證)。能夠看出時間耗掉了1秒,才3000次循環,後面的sys用時也註定咱們用strace不會拿到什麼有效信息。
shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php
shell$ less xxx
咱們只看到這兩次系統調用之間的延時很是大,卻並不知道幹了什麼?束手無策了,幸虧,Linux下的調試利器除了strace還有ltrace(固然還有dtrace,ptrace,不在本文討論範圍了,略去)。
引用:strace用來 跟蹤一個進程的系統調用或信號產生的狀況,而 ltrace用來 跟蹤進程調用庫函數的狀況(via IBM developerworks)。
爲了排除干擾因素,咱們將$x直接賦值爲array(「0″,」1″,」2″,……)的形式,避免過多的malloc調用影響結果。執行
shell$ ltrace -c /usr/local/php/bin/php test.php
如圖2
咱們看到庫函數__strtol_internal的調用很是之頻繁,達到了94%,太誇張了,而後我又查了一下這個庫函數__strtol_internal是幹嗎的,原來是strtol的別名,簡單的說就是把字符串轉換成長整形,能夠猜想PHP引擎已經檢測到這是一個字符串型的數字,因此指望將他們轉換成長整型來比較,這個轉換過程當中消耗了太多時間,咱們再次執行:
shell
複製代碼 代碼以下:緩存
shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php
less
能夠輕鬆抓到大量下圖這樣的調用,到此,問題找到了,in_array這種鬆比較,會將兩個字符型數字串先轉換爲長整型再進行比較,殊不知性能就耗在這上面了。
知道了癥結所在,咱們解決的辦法就不少了,最簡單的就是爲in_array加第三個參數爲true,即變爲嚴格比較,同時還要比較類型,這樣避免了PHP自做聰明的轉換類型,跑起來果真快多了,代碼以下:函數
複製代碼 代碼以下:性能
<?php
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
$x[]= "{$j}";
}測試
for($i=0;$i<3000;$i++){
if(in_array($y,$x,true)){
continue;
}
}
?>
複製代碼 代碼以下:
shell$ time /usr/local/php/bin/php test.php
real 0m0.267s
user 0m0.247s
sys 0m0.020s
快了好多倍啊!!!能夠看到sys耗時幾乎沒有太大變化。咱們再次ltrace一把,仍是要把$x直接賦值,排除malloc調用的干擾,由於咱們實際應用中是從緩存裏一次拉出來的,因此也不存在示例代碼中這樣的循環來申請內存的狀況。
再次執行
複製代碼 代碼以下:
shell$ ltrace -c /usr/local/php/bin/php test.php
以下圖:
__ctype_tolower_loc佔用了最多的時間!查了一下庫函數__ctype_tolower_loc是幹嗎的: 簡單的理解是將字符串轉換成小寫,那麼這說明in_array比較字符串不區分大小寫嗎?其實這個函數調用已經和咱們這個in_array感受聯繫不大 了,關於in_array的實現,仍是去看看PHP的源碼,大概理解的更爲透徹了,好了,無法往下說了,歡迎與我交流,寫的不對的地方請多多斧正。
———————2013.08.29分割線——————————
晚 上又翻了如下PHP 5.4.10的源碼,對in_array的興趣真大啊,哈哈,位於./ext/standard/array.c的第1248行,能夠看到他調用了 php_search_array函數,下面的array_serach也是調的這個,只是最後一個參數不一樣!通過一番跟蹤,在in_array鬆比較的 狀況下,他最終調用的函數 zendi_smart_strcmp(果真是個「聰明」函數)進行比較,位於./Zend/zend_operators.c,咱們用ltrace抓到 的大量轉換成整型的操做就是那個is_numeric_string_ex的行爲。
函數is_numeric_string_ex是在./Zend/zend_operators.h中定義的,在前面進行了一堆的判斷和轉換以後,在232行調用了strtol,就是咱們在文章中提到的系統函數了,將字符串轉換成長整型,有圖有真相