Подтвердить что ты не робот

Оптимизация памяти в массиве PHP

Я работаю с большим массивом, который является картой высот, 1024x1024 и, конечно же, я застрял с лимитом памяти. В моей тестовой машине я могу увеличить лимит памяти до 1 гб, если захочу, но в моем крошечном VPS с 256 барабанами это не вариант.

Я искал в стеке и google и нашел несколько "хорошо, вы используете PHP не потому, что эффективность памяти, это и переписывать в С++", и, честно говоря, это нормально, и я признаю, что PHP любит память.

Но, когда копается больше в управлении памятью PHP, я не нашел, какая память потребляет каждый тип данных. Или, если литье в другой тип данных уменьшает потребление памяти.

Единственный метод "оптимизации", который я нашел, заключался в том, чтобы отключить переменные и массивы, что он.

Преобразование кода в С++ с использованием некоторых парсеров PHP решит проблему?

Спасибо!

4b9b3361

Ответ 1

Если вам нужен настоящий индексированный массив, используйте SplFixedArray. Он использует меньше памяти. Кроме того, PHP 5.3 имеет гораздо лучший сборщик мусора.

Кроме этого, ну, PHP будет использовать больше памяти, чем более тщательно написанный эквивалент C/С++.

Использование памяти для целочисленного массива 1024x1024:

  • Стандартный массив: 218 756 848
  • SplFixedArray: 92,914,208

как измерено memory_get_peak_usage()

$array = new SplFixedArray(1024 * 1024); // array();
for ($i = 0; $i < 1024 * 1024; ++$i)
  $array[$i] = 0;

echo memory_get_peak_usage();

Обратите внимание, что тот же массив в C, использующий 64-битные целые числа, будет 8M.

Как и другие, вы можете упаковать данные в строку. Это медленнее, но much больше эффективности памяти. Если использовать 8-битные значения, это очень просто:

$x = str_repeat(chr(0), 1024*1024);
$x[$i] = chr($v & 0xff); // store value $v into $x[$i]
$v = ord($x[$i]);        // get value $v from $x[$i]

В этом случае память будет составлять примерно 1,5 МБ (т.е. при рассмотрении всех накладных расходов PHP с помощью всего этого массива целых строк).

Для удовольствия от этого я создал простой тест создания 8-разрядных целых чисел 1024x1024, а затем прокручивал их один раз. Все упакованные версии использовали ArrayAccess, чтобы код пользователя выглядел одинаково.

                   mem    write   read
array              218M   0.589s  0.176s
packed array       32.7M  1.85s   1.13s
packed spl array   13.8M  1.91s   1.18s
packed string      1.72M  1.11s   1.08s

В упакованных массивах использовались собственные 64-разрядные целые числа (только для упаковки 7 байтов, чтобы избежать работы с подписанными данными) и упакованной строки, используемой ord и chr. Очевидно, детали реализации и спецификации компьютеров немного повлияют на вещи, но я ожидаю, что вы получите аналогичные результаты.

Итак, в то время как массив был в 6 раз быстрее, он также использовал 125x память как следующую лучшую альтернативу: упакованные строки. Очевидно, что скорость не имеет значения, если у вас заканчивается память. (Когда я использовал упакованные строки непосредственно без класса ArrayAccess, они были только в 3 раза медленнее, чем встроенные массивы.)

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

Ответ 2

В дополнение к принятому ответу и предложениям в комментариях я хотел бы предложить реализацию PHP Judy array.

Быстрые тесты показали интересные результаты. Массив с 1 миллионом записей с использованием обычной структуры данных массива PHP занимает ~ 200 МБ. SplFixedArray использует около 90 мегабайт. Джуди использует 8 мегабайт. Компромисс в производительности, Джуди занимает удвоенное время регулярной реализации массива php.

Ответ 3

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

$array = [];

$data = [];
$data["a"] = "hello";
$data["b"] = "world";

Для хранения этого массива просто используйте:

$array[] = json_encode($data);

вместо

$array[] = $data;

Если вы хотите получить обратно, просто используйте что-то вроде:

$myData = json_decode($array[0], true);

У меня был большой массив с 275 000 наборов и я сэкономил около 36% памяти.

ОБНОВЛЕНИЕ: Я нашел более лучший способ, когда вы застегиваете строку json:

$array[] = gzencode(json_encode($data));

и распакуйте его, когда вам это нужно:

$myData = json_decode(gzdecode($array[0], true));

Это сэкономило мне почти 75% пиковой нагрузки на ОЗУ.