В PHP (>= 5.0), проходит по ссылке быстрее?


в PHP параметры функции могут передаваться по ссылке путем добавления амперсанда к параметру в объявлении функции, например:

function foo(&$bar)
{
    // ...
}

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

вместо этого PHP, похоже, использует Copy On Write, чтобы избежать копирования объектов (и, возможно, также массивов), пока они не будут изменены. Так, для функций, которые не измените их параметры, эффект должен быть таким же, как если бы вы передали их по ссылке.

однако мне было интересно, может ли копия на логике записи быть закорочена при передаче по ссылке и имеет ли это какое-либо влияние на производительность.

ETA: конечно, я предполагаю, что это не быстрее, и я хорошо знаю, что это не то, для чего нужны ссылки. Поэтому я думаю, что мои собственные догадки довольно хороши, я просто ищу ответ от кого-то, кто действительно знает, что такое определенно происходит под капотом. За пять лет разработки PHP мне всегда было трудно получить качественную информацию о PHP internals short от чтения источника.

8 61

8 ответов:

движок Zend использует копирование при записи, и когда вы используете ссылку самостоятельно, он несет немного дополнительных накладных расходов. Можно только найти это упоминание на момент написания статьи, хотя и комментарии в руководство содержать другие ссылки.

(EDIT) страница руководства на объекты и ссылки содержит немного больше информации о том, как переменные объекта отличаются от ссылок.

в тесте с 100 000 итераций вызова функции со строкой 20 кб, результаты:

функция, которая просто читает / использует параметр

pass by value:      0.12065005 seconds
pass by reference:  1.52171397 seconds

функция для записи / изменения параметра

pass by value:      1.52223396 seconds
pass by reference:  1.52388787 seconds

выводы

  1. передать параметр по значению всегда быстрее

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

Я провел некоторый тест на это, потому что я не был уверен в полученных ответах.

мои результаты показывают, что передача больших массивов или строк по ссылке значительно быстрее.

вот мои результаты: Benchmark

ось Y (работает) - это сколько раз функция может быть вызвана за 1 секунду * 10

тест повторялся 8 раз для каждой функции/переменной

и вот переменные, которые я использовал:

$large_array = array_fill(PHP_INT_MAX / 2, 1000, 'a');
$small_array = array('this', 'is', 'a', 'small', 'array');
$large_object = (object)$large_array;
$large_string = str_repeat('a', 100000);
$small_string = 'this is a small string';
$value = PHP_INT_MAX / 2;

это функции:

function pass_by_ref(&$var) {
}

function pass_by_val($var) {
}

я экспериментировал со значениями и ссылками строки 10k байт, передавая ее в две идентичные функции. Один принимает аргумент по значению, а второй по ссылке. Это были обычные функции-взять аргумент, сделать простую обработку и вернуть значение. Я сделал 100 000 вызовов обоих и выяснил, что ссылки не предназначены для повышения производительности - прибыль ссылки была около 4-5%, и она растет только тогда, когда строка становится достаточно большой (100k и дольше, что дало улучшение 6-7%). Итак, мой вывод Не используйте ссылки для повышения производительности, этот материал не для этого.

Я использовал PHP версии 5.3.1

Я почти уверен, что нет, это не быстрее. Кроме того, в руководстве конкретно говорится, что не следует пытаться использовать ссылки для повышения производительности.

Edit: не могу найти, где он говорит, что, но это там!

нет ничего лучше, чем тестовый кусок кода

<?PHP
$r = array();

for($i=0; $i<500;$i++){
$r[]=5;
}

function a($r){
$r[0]=1;
}
function b(&$r){
$r[0]=1;
}

$start = microtime(true);
for($i=0;$i<9999;$i++){
  //a($r);
  b($r);
}
$end = microtime(true);

echo $end-$start;
?>

конечный результат! Чем больше массив (или чем больше количество вызовов), тем больше разница. Поэтому в этом случае вызов по ссылке выполняется быстрее, потому что значение изменяется внутри функции.

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

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

**Pass by Value Test Code:**

$originalString=''; // 1000 pseudo-random digits

function replace($string) {
    return str_replace('1', 'x',$string);
}
$output = '';
/* set start time */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);

for ($i = 0; $i < 10; $i++ ) {
    for ($j = 0; $j < 1000000; $j++) {
        $string = $originalString;
        $string = replace($string);
    }
}

/* report how long it took */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tend = $mtime;
$totalTime = ($tend - $tstart);
$totalTime = sprintf("%2.4f s", $totalTime);
$output .= "\n" . 'Total Time' .
    ': ' . $totalTime;
$output .= "\n" . $string;
echo $output;

проходим по ссылке проверить код

то же самое, за исключением

function replace(&$string) {
    $string = str_replace('1', 'x',$string);
}
/* ... */
replace($string);

результаты в секундах (10 миллион итераций):

PHP 5
    Value:     14.1007
    Reference: 11.5564

PHP 7
    Value:     3.0799
    Reference: 2.9489

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

(Примечание: тесты PHP 7 были выполнены на более быстрой машине - PHP 7 быстрее, но, вероятно, не намного быстрее.)

нет необходимости добавлять & оператор при передаче объектов. В PHP 5+ объекты передаются по ссылке в любом случае.