В первый раз столкнулся со сборкой мусора в пхп. Больно ушибся

Фанат

oncle terrible
Команда форума
Имею экшен в конроллере Симфони.
Экшен должен получить данные из Доктрины, натравить на них небольшую функцию-форматтер, и отдать на в Эксель райтер.

Данных - 20 тысяч строк.

О том, что если получать данные через getResult, то время обработки занимает 35 сек против 6 для getArrayResult() я молчу. Но что меня реально заставило поломать голову, так это профайлинг двух никак не связанных между собой операций:

PHP:
$data = $query->getArrayResult();
$formatted = $this->formatter($data);
$this->excelHelper($formatted);
Выполнение последнего пункта занимает 8 секунд.

PHP:
$formatted = $this->formatter($query->getArrayResult());
$this->excelHelper($formatted);
Выполнение последнего пункта занимает 12 секунд.

Не, общими усилиями разобрались-таки в итоге.
Но поначалу я конкретно подсел на измену.
 

fixxxer

К.О.
Партнер клуба
Ммм, если gc, то должно ж быть не последнего, а _после_ него (при выходе из области видимости)?
 

Вурдалак

Продвинутый новичок
Отдельного внимания заслуживает попытка работать с 20K Doctrine-объектами.
 

Фанат

oncle terrible
Команда форума
Ммм, если gc, то должно ж быть не последнего, а _после_ него (при выходе из области видимости)?
А вот в том-то и дело, что последнего. У него профайлер внутри пишется.
В любом случае, тут не область видимости, как я понимаю, я порог срабатывания коллектора. Поскольку при генерации экселя производится очень много мусора, счетчик зашкаливает, и запускается сборщик.
 

fixxxer

К.О.
Партнер клуба
Чета я все равно не пони. $formatted - это массив, там внутри excelHelper() он меняется, и срабатывает CoW?
 

Фанат

oncle terrible
Команда форума
Чета я все равно не пони. $formatted - это массив, там внутри excelHelper() он меняется, и срабатывает CoW?
Неть :)
Этот массив не меняется, а служит исключительно источником данных для таблицы.
В этом-то вся и загадка была. Тут именно что не CoW, а GC.
Ну, по крайней мере, я так думаю. В любом случае, если бы это было что-то связанное с массивом, то время выполнения оставалось одинаковым. А тут блин меняется!

Еще один эксперимент, который я провел:
PHP:
$data = $query->getArrayResult();
$formatted = $this->formatter($data);
unset($data);
$this->excelHelper($formatted);
Сам по себе ансет времени не занимает, но время формирование экселя увеличивается!
Это приводит меня к мысли о сборщике мусора.
Насколько я понимаю его работу, он тупо считает ссылки на мусорные структуры. И запускается только если достигнут определенный порог. Но если уж запускается - то мало не покажется, вся работа останавливается.

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

В любом случае, добавление gc_disable() в эксель хелпер решило проблему.
 

fixxxer

К.О.
Партнер клуба
А. Ну вроде логично, да.

А все потому что вместо нормального generational GC нагородили рефкаунт с костылем для разрыва циклов
 
Сверху