Сохраняет ли сбрасывание значений массива во время итерации память? - PullRequest
8 голосов
/ 13 января 2011

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

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

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

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

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

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

    $formattedData[] = $reformattedVal;

    unset($myData[$key]);
}

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

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

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

Ответы [ 5 ]

4 голосов
/ 13 января 2011

Используйте ссылку на переменную в цикле 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;
3 голосов
/ 01 февраля 2013

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

while($data = array_pop($xml_data)){
     //process $data
}
2 голосов
/ 13 января 2011

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

  1. Первое правило Клуба оптимизации - вы не оптимизируете.
  2. Второе правило Клуба оптимизации - вы не оптимизируете без измерения.
  3. Если ваше приложение работает быстрее, чем базовый транспортный протокол, оптимизация завершена.
  4. Один фактор за раз.
  5. Нет маркетроидов, нет расписаний маркетроидов.
  6. Тестирование будет продолжаться столько, сколько потребуется.
  7. Если это ваша первая ночь в Клубе оптимизации, вы должны написать контрольный пример.

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

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

2 голосов
/ 13 января 2011

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

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

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

0 голосов
/ 13 января 2011

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

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

...