Уменьшение использования памяти для массива и SplFixedArray

Я делал некоторые тесты между array() и SplFixedArray() и я сталкивался с странным поведением. Сначала рассмотрим мой простой тест (это просто отредактированная версия из Интернета, извините, я не могу найти исходный исходный код сейчас):

 function formatMemoryUsage($usage) { $unit = array(' B', 'kB', 'MB', 'GB', 'TB'); $factor = floor((strlen($usage) - 1) / 3); return sprintf('%.2f %s (%d bytes) ', $usage / pow(1024, $factor), $unit[$factor], $usage); } for($size = 1000; $size < 100000; $size *= 2) { echo PHP_EOL . '> Testing size: ' . number_format($size) . PHP_EOL; echo ' Array()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = Array(), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); echo ' SplFixedArray()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = new SplFixedArray($size), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); } с function formatMemoryUsage($usage) { $unit = array(' B', 'kB', 'MB', 'GB', 'TB'); $factor = floor((strlen($usage) - 1) / 3); return sprintf('%.2f %s (%d bytes) ', $usage / pow(1024, $factor), $unit[$factor], $usage); } for($size = 1000; $size < 100000; $size *= 2) { echo PHP_EOL . '> Testing size: ' . number_format($size) . PHP_EOL; echo ' Array()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = Array(), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); echo ' SplFixedArray()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = new SplFixedArray($size), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); } с function formatMemoryUsage($usage) { $unit = array(' B', 'kB', 'MB', 'GB', 'TB'); $factor = floor((strlen($usage) - 1) / 3); return sprintf('%.2f %s (%d bytes) ', $usage / pow(1024, $factor), $unit[$factor], $usage); } for($size = 1000; $size < 100000; $size *= 2) { echo PHP_EOL . '> Testing size: ' . number_format($size) . PHP_EOL; echo ' Array()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = Array(), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); echo ' SplFixedArray()' . PHP_EOL; for($s = microtime(true), $m = memory_get_usage(true), $container = new SplFixedArray($size), $i = 0; $i < $size; $i++) $container[$i] = null; echo ' - Write - time : ' . str_pad(microtime(true) - $s, 20, '0') . ' - memory: ' . formatMemoryUsage(memory_get_usage(true) - $m) . PHP_EOL; $s = microtime(true); foreach ($container as $key => $value) { $void = $value; } echo ' - Read - time : ' . str_pad(microtime(true) - $s, 20, '0') . PHP_EOL; unset($container); } 

Результаты были ожидаемыми – SplFixedArray() был быстрее в письменной форме и немного медленнее при чтении. Вещи начинают странно, когда я устанавливаю другой SplFixedArray() сразу после unset() предыдущего, см. Вывод:

 > Testing size: 64,000 Array() - Write - time : 0.009041070938110400 - memory: 7.50 MB (7864320 bytes) - Read - time : 0.004010915756225600 SplFixedArray() - Write - time : 0.004639148712158200 - memory: 1.75 MB (1835008 bytes) - Read - time : 0.005971908569335900 SplFixedArray() - Write - time : 0.005653858184814500 - memory: 1.50 MB (1572864 bytes) - Read - time : 0.006288051605224600 

Почему второй тест использует меньше памяти, чем первый? И эй, я пытаюсь добавить следующий тест и:

 > Testing size: 64,000 Array() - Write - time : 0.008963823318481400 - memory: 7.50 MB (7864320 bytes) - Read - time : 0.004142045974731400 SplFixedArray() - Write - time : 0.005026102066040000 - memory: 1.75 MB (1835008 bytes) - Read - time : 0.005756139755249000 SplFixedArray() - Write - time : 0.004483938217163100 - memory: 1.50 MB (1572864 bytes) - Read - time : 0.005591869354248000 SplFixedArray() - Write - time : 0.004633903503418000 - memory: 1.25 MB (1310720 bytes) - Read - time : 0.005697011947631800 

Поэтому я, конечно же, стараюсь добавлять все больше и больше, а затем несколько уменьшаться на 512 КБ . Мой вопрос здесь очевиден: как это возможно и почему, когда я отключил предыдущий объект и создаю новый, используемая память ниже? И он также работает с обычным array() .

Нам нужно зайти в двигатель, если мы хотим это понять. Лучше: внутри Zend / zend_alloc.c (Менеджер памяти Zend) .

Память, выделяемая диспетчером памяти, разделяется на куски размером 256 КБ .

После освобождения SPLFixedArray освобождается только первый непрерывный фрагмент памяти . Всегда остается блок размером 256 КБ (некоторые переменные), который затем накапливается. (который работает как следующий из выделенной ОС памяти, будет просто рядом с этим блоком памяти)

Этот сегмент памяти затем помечен как свободный и используется в следующий раз, когда это возможно, вместо выделения новой памяти из ОС. (и при необходимости добавляется некоторая память)

Но поскольку по крайней мере один блок из 256 КБ всегда свободен, мы всегда замечаем разницу в 256 КБ.


Когда вы хотите измерить использование памяти, я бы действительно использовал memory_get_usage(false) как это указывает на то, сколько памяти требуется PHP (≠ Zend). (единственное, что имеет значение против установки memory_limit ini)