在高頻呼叫的函式中,無心插入的參考賦值 $timequeue = &$arr['timequeue'] 導致處理 22 MB 資料從 10 秒飆升至 40 秒。根因在於 參考標記改變了 zval 類型,破壞了 Copy-on-Write,迫使 count() 每次都複製整個陣列。
function update_timelist(&$arr, $timestamp, $threshold) {
$timequeue = &$arr['timequeue']; // 問題在這一行
while (!empty($timequeue[0]) && ($timestamp - $timequeue[0]) > $threshold) {
array_shift($timequeue);
}
array_push($timequeue, $timestamp);
if ($arr['count'] < count($timequeue)) { // count() 變慢
$arr['count'] = count($timequeue);
}
}
function update_timelist(&$arr, $timestamp, $threshold) {
while (!empty($arr['timequeue'][0]) && ($timestamp - $arr['timequeue'][0]) > $threshold) {
array_shift($arr['timequeue']);
}
array_push($arr['timequeue'], $timestamp);
if ($arr['count'] < count($arr['timequeue'])) {
$arr['count'] = count($arr['timequeue']);
}
}
執行時間從 40 秒降至 10 秒。
$arr['timequeue'] 為非參考 zval(is_ref=0)。count() 讀取時觸發 COW,但因無寫入,不複製,僅增加 refcount,極快。& 後:$timequeue 標記為 is_ref=1。PHP 規定同一 zval 不能同時為參考與非參考,故必須立即複製一份給原陣列元素,$timequeue 持有新副本(參考),原陣列持有舊副本(非參考)。count($timequeue) 時:count 內部要求非參考 zval,發現參數是參考類型,再次複製整個陣列以滿足非參考需求。每次循環都複製 → O(n²) 災難。array_shift、array_push 等陣列函式參數本身就是參考傳遞(函式簽名為 array_shift(array &$array)),直接傳 $arr['timequeue'] 即可修改原陣列,不需要額外加 &。xdebug_debug_zval('var') 可查看 refcount、is_ref 狀態。$arr['key'],勿建立中介參考變數。count()、sizeof() 重複呼叫,預存長度或用 for 迴圈。WeakReference 或物件識別符替代強參考,減少複製壓力。