Дают ли выигрыш ссылки на элементы массива и на поля объектов в PHP?

Из описания ссылок в документации php не очень понятно, дает ли какой-то выигрыш в производительности использование ссылок на элементы массивов, на поля объектов.

Например, есть ли преимущество в производительности такого кода
$ref = &$obj->arr[$i];

$ref->field_1 = val_1;
$ref->field_2 = val_2;
...
$ref->field_n = val_n;
в сравнении с таким
$obj->arr[$i]->field_1 = val_1;
$obj->arr[$i]->field_2 = val_2;
...
$obj->arr[$i]->field_n = val_n;


Быть может, при доступе по ссылке не нужно каждый раз вычислять адрес промежуточного объекта $obj->arr[$i]? Или система доступа к переменным php такова, что ссылки не дают такой экономии?
  • Вопрос задан
  • 289 просмотров
Пригласить эксперта
Ответы на вопрос 4
Fockker
@Fockker Куратор тега PHP
Потомок старинного рода Ипатьевых-Колотитьевых
С точки зрения памяти не даёт, потому что в РНР и так очень оптимальное управление памятью.
В частности, при присвоении значений переменным применяется принцип copy-on-write. Объекты же и так всегда копируются по ссылке.

С точки зрения "вычислять адрес промежуточного объекта" - я никогда не слышал о таких проблемах. Судя по всему, это явно не является узким местом для динамически интерпретируемого языка.
В любом случае, учитывая copy-on-write, исходно это снова будет опять ссылка.

В общем, я настоятельно рекомендую последовать совету мудрого старика Дональда Кнута, и не бежать впереди паровоза, заранее пытаясь оптимизировать то, что вообще никогда не тормозит.
Данный случай является просто идеальной иллюстрацией именно такого случая, когда проблемы от непредсказуемого поведения кода из-за пихания ссылок везде и всюду стократно превысят любые воображаемые плюсы.
Ответ написан
SilenceOfWinter
@SilenceOfWinter Куратор тега PHP
та еще зажигалка...
все зависит от ситуации. в большинстве случаев дает т.к. ссылка занимает меньше места в памяти, чем значение.
Ответ написан
Комментировать
gzhegow
@gzhegow
aka "ОбнимиБизнесмена"
Имел опыт прохождения некоего теста, где обход какого-то там 100 тысяч вложенности массива должен был укладываться в какие-то миллисекунды. Долбался тогда долго, но помню что без ссылок не тащило. Там в итоге получился обход стеком или там очередью не помню уже, но смысл был в том, что вместо того, чтобы "добавлять в очередь" в определенном порядке старые значения менялись на новые избегая операции push()/unset() пытаясь на этом "сэкономить".

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

У ссылок скорее другая полезная нагрузка.

В пхп массив это не объект в каком-то смысле, а скаляр. При присваивании его он копируется, как строка.

Бывает нужно получить по пути в массиве элемент, а потом поменять его, не соединяя нечто через $arr[ $a ][ $b ][ $c ], т.к. ваши $a,$b,$c представляют собой строку типа 'hello.foo.bar'. Вот тут удобно вернуть ссылку, воткнуть туда что-то новое, не занимаясь обходом снова и снова. Справедливости ради можно сделать:
[ $a, $b, $c ] = explode('.', $varname);
$arr[ $a ][ $b ][ $c ] = 1;

Но если число уровней переменное - приехали

Ещё ссылки неоценимо помогают с пхпшными замыканиями, которые как известно присваивают не скоуп функции их вызвавшей, а скоуп всего класса или вообще ничего. Передав через use(&$some) в него можно записывать изнутри замыкания, т.к. переменные окружающие внутри будут не видны.

А еще при создании \Closure забыв указать static спереди этот обьект оказывается привязан к замыканию. И потом "совершенно случайно" кто-то вызывает вашу функцию в цикле, которая создает замыкания, та в свою очередь копирует туда обьект (вроде как привязывает ссылку! но внутренние массивы лежащие в protected $property так не думают, а стало быть клонирует), имеющий массив с войной-и-миром занимающий мегабайт, и откуда-то получается 100 мегабайт занятой памяти, потому как 100 колбэков было создано. То есть тут еще кое-что про сборщик мусора надо добавить, что к примеру он не может удалить из памяти обьекты ссылающиеся друг на друга и удалит их только когда наберется некое число их очень большое. Именно эта проблема там и играет. Что при клонировании объектов свойства содержащие скаляры - копируются. В этом свои плюсы, например не надо как JavaScript постоянно изобретать deepClone(), т.к. полезная нагрузка уже является копией, а конкретные обьекты можно перетыкать ручками, но чаще всего это сервисы и они общие для всех экземпляров. Но случайно можно там накопировать делов.

ps. некогда пытался склонировать доктриновскую энтити, ибо задача буквально такой и была - сделать "клон" записи (там были опросы, ответы и варианты ответов). боль связанная с тем, что в доктриновских связях более чем первого уровня живут объекты-ожидалки, которые выдадут данные только если их пнуть - просто незабываема. Они делали оптимизацию, чтобы не тянуть полбазы данных. Но теперь чтобы что-то скопировать - можно сдохнуть.
Ответ написан
Комментировать
@galliard
В данном случае не нужны никакие ссылки, просто делаете так:
$ref = $obj->arr[$i];

$ref->field_1 = val_1;
$ref->field_2 = val_2;
...
$ref->field_n = val_n;

И работать будет быстрее, и памяти меньше сожрет.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы