В PHP (> = 5.0) передача по ссылке быстрее? - PullRequest
63 голосов
/ 07 октября 2008

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

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

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

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

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

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

Ответы [ 9 ]

70 голосов
/ 02 октября 2010

В тесте с 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. Если функция изменяет значение передаваемой переменной, то для практических целей она аналогична передаче по ссылке, чем по значению

33 голосов
/ 07 октября 2008

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

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

27 голосов
/ 05 июля 2011

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

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

Вот мои результаты: 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) {
}
6 голосов
/ 01 октября 2011

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

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

4 голосов
/ 07 октября 2008

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

Редактировать: Не могу найти, где написано, но оно есть!

1 голос
/ 27 июля 2014

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

<?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;
?>

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

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

0 голосов
/ 25 февраля 2019

Это просто, не нужно ничего проверять. Зависит от варианта использования.

Передача по значению всегда будет быстрее по значению, чем ссылка для небольшого количества аргументов. Это зависит от того, сколько переменных эта архитектура позволяет передавать через регистры (ABI).

Например, x64 позволит вам передавать через регистры 4 64-битных значения. https://en.wikipedia.org/wiki/X86_calling_conventions

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

Если ваши данные, которые нужно передать, больше, чем ABI, остальные значения будут помещены в стек. В этом случае массив или объект (который в данном случае является классом или структурой + заголовками) ВСЕГДА БУДЕТ БЫСТРЕЕ ПО ССЫЛКЕ.

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

PHP написан на C / C ++, поэтому я ожидаю, что он будет вести себя так же.

0 голосов
/ 19 февраля 2018

Я попытался сравнить это с реальным примером, основанным на проекте, над которым я работал. Как всегда, различия тривиальны, но результаты оказались несколько неожиданными. Для большинства тестов, которые я видел, вызываемая функция на самом деле не изменяет переданное значение. Я выполнил простую функцию 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 быстрее, но, вероятно, не намного быстрее.)

0 голосов
/ 07 октября 2008

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

...