Отбрасывает ли значения массива во время итерации сохранения в памяти?

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

$myData = array('Key1' => array('value1', 'value2'));

Но я бы хотел что-то вроде:

$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2'))));

Итак, я беру первый $myData и отформатирую его как второй $myData. Я полностью согласен с моим алгоритмом форматирования. Мой вопрос заключается в том, чтобы найти способ сохранить память, поскольку эти массивы могут стать немного громоздкими. Таким образом, во время цикла foreach я копирую текущие значения массива в новый формат, затем меняю значение, с которым я работаю, из исходного массива. Например:.

$formattedData = array();
foreach ($myData as $key => $val) {
 // do some formatting here, copy to $reformattedVal
 $formattedData[] = $reformattedVal;
 unset($myData[$key]);
}

Является ли вызов unset() хорошей идеей здесь? I.e., он сохраняет память, так как я скопировал данные и больше не нуждаюсь в исходном значении? Или PHP автоматически мусор собирает данные, так как я не ссылаюсь на него в любом последующем коде?

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

Спасибо за любые идеи. -SR

5 ответов

Используйте ссылку на переменную в цикле foreach, используя оператор &. Это позволяет сделать копию массива в памяти для foreach для повторения.

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

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

Используйте memory_get_usage(), чтобы определить, сколько памяти вы используете.

Существует хорошая запись об использовании и распределении памяти здесь.

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

echo memory_get_usage() . PHP_EOL;
$test = $testCopy = array();
$i = 0;
while ($i++ < 100000) {
 $test[] = $i;
}
echo memory_get_usage() . PHP_EOL;
foreach ($test as $k => $v) {
//foreach ($test as $k => &$v) {
 $testCopy[$k] = $v;
 //unset($test[$k]);
}
echo memory_get_usage() . PHP_EOL;


Пожалуйста, помните правила Оптимизационного клуба:

  • Первое правило Optimization Club - это не оптимизация.
  • Второе правило Клуба Оптимизации - это не оптимизация без измерения.
  • Если ваше приложение работает быстрее, чем базовый транспортный протокол, оптимизация завершена.
  • Один фактор за раз.
  • Нет рыночных мер, нет графиков рынка.
  • Тестирование будет продолжаться до тех пор, пока оно должно быть.
  • Если это ваша первая ночь в Оптимизационном клубе, вам нужно написать тестовый пример.

Правила № 1 и № 2 здесь особенно актуальны. Если вы не знаете, что вам нужно оптимизировать, и если вы не определили, что нужно оптимизировать, тогда не делайте этого. Добавление unset добавит вред во время выполнения и заставит будущих программистов, почему вы это делаете.

Оставьте это в покое.


Если в любой момент в "форматировании" вы делаете что-то вроде:

$reformattedVal['a']['b'] = $myData[$key];

Тогда выполнение unset($myData[$key]); не имеет отношения к памяти, потому что вы уменьшаете счетчик ссылок переменной, которая теперь существует в двух местах (внутри $myData[$key] и $reformattedVal['a']['b']). Фактически, вы сохраняете память для индексирования переменной внутри исходного массива, но это почти ничего.


У меня закончилась нехватка памяти при обработке строк текстового файла (xml) в цикле. Для всех, у кого схожая ситуация, это сработало для меня:

while($data = array_pop($xml_data)){
 //process $data
}


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

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

licensed under cc by-sa 3.0 with attribution.