Intereting Posts

Почему так медленно вызывается функция (например, strlen, count и т. Д.) По ссылочному значению?

Я только что нашел что-то очень странное в PHP.

Если я передаю переменную функции по ссылке, а затем вызову функцию на ней, она невероятно медленная.

Если вы зацикливаете вызов внутренней функции и переменная велика, она может быть на несколько порядков медленнее, чем если переменная передается по значению.

Пример:

<?php function TestCount(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "took $fTaken seconds\n"; } $aArray = array(); TestCount($aArray); ?> 

Для работы на моей машине требуется примерно 20 секунд (на PHP 5.3).

Но если я меняю функцию на передачу по значению (т. function TestCount($aArray) вместо function TestCount(&$aArray) ), то она работает примерно в 2 мс – буквально в 10 000 раз быстрее !

То же самое относится к другим встроенным функциям, таким как strlen , и к пользовательским функциям.

Что происходит?

Я нашел отчет об ошибке с 2005 года, в котором описывается именно эта проблема: http://bugs.php.net/bug.php?id=34540

Таким образом, проблема заключается в том, что при передаче ссылочного значения функции, которая не принимает ссылку, PHP должен ее скопировать.

Это можно продемонстрировать с помощью этого тестового кода:

 <?php function CalledFunc(&$aData) { // Do nothing } function TestFunc(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { CalledFunc($aArray); } $fTaken = microtime(true) - $fStartTime; print "took $fTaken seconds\n"; } $aArray = array(); TestFunc($sData); ?> 

Это выполняется быстро, но если вы измените function CalledFunc(&$aData) на function CalledFunc($aData) вы увидите аналогичное замедление к примеру count .

Это довольно тревожно, так как я долгое время программировал PHP, и я понятия не имел об этой проблеме.

К счастью, во многих случаях это довольно простое решение – используйте временную локальную переменную внутри цикла и скопируйте ее в ссылочную переменную в конце.

Итак, принимая ваш ответ уже, вы можете частично избежать этой проблемы, заставив копию до итеративной работы (Копирование после этого, если данные будут изменены).

 <?php function TestCountNon($aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "Non took $fTaken seconds\n<br>"; } function TestCount(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "took $fTaken seconds\n<br>"; } function TestCountA(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); $bArray = $aArray; for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($bArray); } $aArray = $bArray; $fTaken = microtime(true) - $fStartTime; print "A took $fTaken seconds\n<br>"; } $nonArray = array(); TestCountNon($nonArray); $aArray = array(); TestCount($aArray); $bArray = array(); TestCountA($bArray); ?> не <?php function TestCountNon($aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "Non took $fTaken seconds\n<br>"; } function TestCount(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($aArray); } $fTaken = microtime(true) - $fStartTime; print "took $fTaken seconds\n<br>"; } function TestCountA(&$aArray) { $aArray = range(0, 100000); $fStartTime = microtime(true); $bArray = $aArray; for ($iIter = 0; $iIter < 1000; $iIter++) { $iCount = count($bArray); } $aArray = $bArray; $fTaken = microtime(true) - $fStartTime; print "A took $fTaken seconds\n<br>"; } $nonArray = array(); TestCountNon($nonArray); $aArray = array(); TestCount($aArray); $bArray = array(); TestCountA($bArray); ?> 

Результаты:

 Non took 0.00090217590332031 seconds took 17.676940917969 seconds A took 0.04144287109375 seconds 

Не совсем так хорошо, но чертовски много лучше.