PHP 引用是個(gè)壞習(xí)慣
更新時(shí)間:2010年03月12日 20:37:49 作者:
在寫PHP 程序的時(shí)候,很多人在傳遞參數(shù)的時(shí)候,喜歡用一個(gè)引用。特別是在一個(gè)數(shù)組非常的大的時(shí)候,更是喜歡加。
復(fù)制代碼 代碼如下:
function binsearch(&$arr, $key, $value)
{
$low = 0;
$high = count($arr);
while ($low <= $high) {
$mid = floor($low + ($high - $low) / 2);
$item = $arr[$mid][$key];
if ($item == $value) {
return $mid;
} else if ($value > $item) {
$low = $mid + 1;
} else {
$high = $mid - 1;
}
}
return false;
}
在這里,$mid 采用了先減后加的方法計(jì)算,目的是為了防止整數(shù)的溢出。不是故意寫復(fù)雜了。
我用下面的代碼進(jìn)行測試:
復(fù)制代碼 代碼如下:
$data = array();
for ($i = 0; $i < 1000000; $i++)
{
$data[] = array("sq" => $i * 2);
}
var_dump(binsearch($data, "sq", 10000));
發(fā)現(xiàn),binsearch 的時(shí)候,總是要花個(gè) 0.2s左右。理論上來說,100萬的數(shù)據(jù),最多也就是循環(huán)20次。怎么會這樣慢呢。
后來監(jiān)控了一下內(nèi)存,data 數(shù)組 占用了 230M 的內(nèi)存。而 binsearch 的時(shí)候,占用了60K 的內(nèi)存。但是,理論上來說,binsearch
不應(yīng)該占用如此多的內(nèi)存。因?yàn)?,我覺得,我已經(jīng)用引用了,根本就沒有對data 的結(jié)構(gòu)進(jìn)行修改。
我也是百思不得其解,后來,我把引用參數(shù)去掉,居然 binsearch 只要 0.0002s ,看來是引用耗費(fèi)了大量的cpu 資源。
PHP 內(nèi)部遵循一個(gè)copy on write 的原則。實(shí)際上這個(gè)引用是多余的。
但是為什么,加了引用速度會變慢呢?今天重點(diǎn)就談?wù)勥@個(gè)問題。明白道理后,大家一定知道怎么用引用了。
如果在binsearch 調(diào)用前,直接 $a = &$data,這個(gè)引用的速度會非常的快??磥砜隙ú皇且帽旧懋a(chǎn)生的問題。
這個(gè)問題,實(shí)際上涉及了zend 引擎如何管理PHP變量。
先看下面的問題:
復(fù)制代碼 代碼如下:
<?php
function demo(&$a, &$b) { $a =& $b; }
$a = 1;
$b = 2;
demo($a, $b);
$b = 3;
print $a;
?>
$a 輸出是多少呢?不錯(cuò),是2. 不過,我一開始覺得是3。
那么怎么解釋上面這個(gè)問題呢?
實(shí)際上,函數(shù)的參數(shù)引用是這樣進(jìn)行的。
復(fù)制代碼 代碼如下:
$tmp = $a;
$a1 = &$tmp;
$a = $tmp;
unset($a1, $tmp);
這里,引用的實(shí)際上是一個(gè)臨時(shí)變量。這個(gè)時(shí)候,$tmp 是帶引用屬性的,而$a 變量不是帶引用屬性的。
根據(jù)zend引擎管理內(nèi)存的方法,在內(nèi)部,不能用一個(gè)zval 來表示,必須強(qiáng)制分離這個(gè)zval。
用這樣的理解方法,上面的問題就解決了。函數(shù)內(nèi)部,不會改變函數(shù)外部的引用特性。這也是PHP
不贊成用 calltime_by_ref 的原因,而選擇上面如此低效的拷貝方法。
下面的分析,也能證明,在傳遞參數(shù)時(shí),的確發(fā)生了拷貝。
在 binsearch 函數(shù)里面。
$data[0] = 1;
這樣,就會發(fā)生一次$data 所在zval 的拷貝。內(nèi)存使用量 就是 60K。和函數(shù)調(diào)用加引用一模一樣。
可能很多人會疑問,為什么不是多了230M呢,這其實(shí)就是PHP的高明之處,數(shù)組Key 對應(yīng)的是一個(gè)zval的指針。(內(nèi)部是一個(gè)哈希表)
所以,只要把這些指針復(fù)制一遍就就好了,數(shù)據(jù)不用復(fù)制。但是,100萬的PHP 哈希表實(shí)際上要占用 50M 內(nèi)存。為什么只有60K呢。
在 binsearch 函數(shù)的外面,運(yùn)行
復(fù)制代碼 代碼如下:
$t = $data;
$t[0] = 1;
unset($t);
果然,多了60K 的內(nèi)存。估計(jì)和PHP的內(nèi)存管理機(jī)制有關(guān)系。
現(xiàn)在一切都明白了吧!今天,想了好幾個(gè)小時(shí),才把這個(gè)問題想通,不敢獨(dú)享。
函數(shù)中的引用不是給你傳參數(shù)方便的,而是讓你實(shí)現(xiàn),一個(gè)函數(shù),可以有多個(gè)返回值的,所以,最好不要畫蛇添足。
實(shí)際上,用引用它會降低性能。
相關(guān)文章
PHP實(shí)現(xiàn)RabbitMQ消息列隊(duì)的示例代碼
眾所周知,php本身的運(yùn)行效率存在一定的缺陷,所以如果有一個(gè)很復(fù)雜很耗時(shí)的業(yè)務(wù)時(shí),必須開發(fā)一個(gè)常駐內(nèi)存的程序。本文將利用PHP實(shí)現(xiàn)RabbitMQ消息列隊(duì),感興趣的可以了解一下2022-05-05
PHP使用內(nèi)置函數(shù)file_put_contents寫入文件及追加內(nèi)容的方法
這篇文章主要介紹了PHP使用內(nèi)置函數(shù)file_put_contents寫入文件及追加內(nèi)容的方法,結(jié)合實(shí)例形式分析了file_put_contents函數(shù)通過參數(shù)設(shè)置實(shí)現(xiàn)寫入文件及追加內(nèi)容的相關(guān)技巧,非常簡單實(shí)用,需要的朋友可以參考下2015-12-12
php+js實(shí)現(xiàn)的無刷新下載文件功能示例
這篇文章主要介紹了php+js實(shí)現(xiàn)的無刷新下載文件功能,結(jié)合實(shí)例形式分析了php無刷新下載文件的相關(guān)原理、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-08-08
PHP中輸出轉(zhuǎn)義JavaScript代碼的實(shí)現(xiàn)代碼
最近在做天地圖是GIS集成··要輸出HTML到JavaScript里面··涉及到代碼轉(zhuǎn)義什么的比較麻煩··所以寫個(gè)PHP的function2011-04-04
PHP GD 圖像處理組件的常用函數(shù)總結(jié)
如今,在互聯(lián)網(wǎng)上,很多網(wǎng)站都要處理大量圖片,比如:頭像、上傳的圖片做縮略圖加水印等等。他們需要服務(wù)端的大量圖片處理,一門好的服務(wù)端語言環(huán)境一定要有對圖像處理的支持。2010-04-04
PHP異常Parse error: syntax error, unexpected T_VAR錯(cuò)誤解決方法
在PHP中根本不需要使用var聲明的,但是當(dāng)一個(gè)變量作為一個(gè)類的成員變量的時(shí)候,使用var還是沒有問題的2014-05-05

