Я сталкиваюсь с большим потреблением памяти в Php 7 по сравнению с PHP 5.6

когда я делал тест, я обнаружил, что PHP 7 использует больше памяти, чем PHP 5.6.

Итак, я сделал тест. Я запустил скрипт, содержащий только:

  $a=10;

и ниже приведены результаты для памяти, используемой при использовании PHP CLI без каких-либо модулей (php -n)

php 5.6 = 222600 Bytes
php 7.0 = 350448 Bytes

* PHP 5.6.23 (cli) (built: Jun 22 2016 12:13:15)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies 

* PHP 7.0.9 (cli) (built: Jul 20 2016 10:47:41) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

среда

  • ОС: окно 10
  • сервер: IIS (хотя я использовал CLI, а не сервер), с быстрым cgi
  • машина : 64 бит
  • РНР-5.6.23-НЦ-с Win32-VC11-x64 с
  • php-7.0.9-nts-Win32-VC14-x64

может кто-нибудь объяснить, почему я получил этот результат?


Дополнительные Тесты

используя этот код, как предложил @gordon,

$i=0;
while ($i++ < 100000) ;

php 5.6: 227408 байт

php 7.0: 386640 байт

я определил использование памяти с помощью этого кода:

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();

4 ответов


чтобы понять ответ на ваш вопрос - вам нужно понять, как PHP5 и PHP7 выделяют память.

PHP5 выделение памяти "по запросу", предполагая, что это структура Zend Engine.

на PHP7 это некоторые оптимизации, сделанные на этой стороне, поэтому на выделение памяти "кусками"

  • при запуске он выделяет большой кусок памяти
  • при распределении в приложении он выделяет небольшой кусок, чтобы избежать фрагментация

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

и да, PHP7 сохраняет память очень много на больших программах.

вы можете просмотреть все эти различия в картинках ниже:

PHP memory allocation for large programs PHP memory allocation for small programs

графика, построенная с помощью benchmark: 1.в PHP

<?php

ini_set('memory_limit', '5G');
$a=range(1,$argv[1]);

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();
echo PHP_EOL;

bench.sh

// Small programs
(for i in $(seq 0 5 5000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m
(for i in $(seq 0 5 5000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m
//Large Programs
(for i in $(seq 0 50 100000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m    
(for i in $(seq 0 50 100000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m

октавы ящика

php7;php7=ans;
php5;php5=ans;
plot(php5(:,5)',[php5(:,1:4)';php7(:,1:4)']');
legend("PHP5 mgu", "PHP5 rmu", "PHP5 rpmu", "PHP5 pmu","PHP7 mgu", "PHP7 rmu", "PHP7 rpmu", "PHP7 pmu");

подробнее

  1. официальная презентация PHP7/PHP-NG: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
  2. официальный PHP7 / PHP-NG описание внутренних изменений: https://wiki.php.net/phpng-int
  3. официальное руководство по миграции расширения : https://wiki.php.net/phpng-upgrading
  4. хорошие статьи от @NikiC: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html и http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2
  5. внутренние детали PHP5: http://www.phpinternalsbook.com/
  6. Badoo PHP5 - >история успеха PHP7 с деталями: https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/

ваши тесты показывают больше использования памяти в PHP 7.0, потому что код тестирования очень прост.

PHP 7.0, как известно, использует меньше памяти (и быстрее), что PHP 5.6 из-за радикальной перезаписи внутреннего движка ZEND (ядро интерпретатора)

As Гордон прокомментировал, скорее всего, новые функции и улучшения в PHP 7.0 требуют "загрузки" это приводит к отрицательным результатам при тестировании на небольших фрагментах кода.

давайте попробуем это с чем-то более сложным: создайте массив из 10.000 целых чисел, а затем отсортируйте его с помощью Quicksort алгоритм.

вот результат, который я получаю:

PHP 7.0

Memory Usage: 1432752
Real Memory Usage: 4194304
Real Peak Memory Usage: 4194304
Peak Memory Usage: 3152360


PHP 5.6

Memory Usage: 2756744
Real Memory Usage: 4980736
Real Peak Memory Usage: 6029312
Peak Memory Usage: 5710464

и все же простой 20 строк quicksort далек от реальных приложений с тысячами строк кодов, многих объявлений классов, многих экземпляров...

Я запустил тест на http://phptester.net

ниже код

<?php
function quick_sort($array)
{
    $length = count($array);
    $pivot = $array[0];
    $left = $right = array();
    for($i = 1; $i < count($array); $i++)
    {
        if($array[$i] < $pivot)
        {
            $left[] = $array[$i];
        }
        else
        {
            $right[] = $array[$i];
        }
    }
    return array_merge(quick_sort($left), array($pivot), quick_sort($right));
}

$unsorted = array();
for($i=0;$i<10000;$i++)
{
    $unsorted[] = rand(1,1000000);
}

$sorted = quick_sort($unsorted);

$lf = "<br/>";

echo $lf;
echo "Memory Usage: ".memory_get_usage();
echo $lf;
echo "Real Memory Usage: ".memory_get_usage(true);
echo $lf;
echo "Real Peak Memory Usage: ".memory_get_peak_usage(true);
echo $lf;
echo "Peak Memory Usage: ".memory_get_peak_usage();
echo $lf;

кредит для алгоритма quicksort в PHP:http://andrewbaxter.net/quicksort.php


заранее я хочу сказать, что если вы видите более высокое использование памяти в PHP 7 на реальном коде, наиболее вероятной причиной является то, что PHP 7 сообщит об использовании памяти буферизованных запросов mysqlnd как часть использования памяти. В PHP 5 об этом использовании памяти не сообщалось (но, конечно, память все еще использовалась). Для больших запросов это может иметь очень существенное значение.

теперь к вашему фактическому случаю, который в основном является использованием памяти PHP сразу после запуска запроса. Этот ответ MobDev уже объясняет, почему существует несоответствие в" реальном " использовании памяти, которое является метрикой использования памяти, которая сообщает, сколько памяти PHP-распределитель запросил у системного распределителя ядра. Как отмечает MobDev, PHP 7 будет выделять память в гораздо больших кусках (2 МБ), а также более агрессивно относится к кэшированию выделенных кусков.

однако это не объясняет расхождение в "не-реальной" памяти, которые не принимают эти выделения подробности в расчет. Легко проверить, идет ли точно память с помощью профилировщика памяти, например, путем запуска PHP через USE_ZEND_ALLOC=0 valgrind --tool=massif. The USE_ZEND_ALLOC=0 часть инструктирует PHP не использовать собственный распределитель.

прежде всего это покажет вам, что фактическое использование памяти и использование памяти, о которых сообщает PHP, значительно отличаются. Massif покажет использование 3.2 MB для PHP 5.6 и 2.3 MB для PHP 7. Причина в том, что PHP сообщает только о памяти, которая проходит через собственный распределитель (ZMM), в то время как многие структуры, которые выживают в нескольких запросах, не выделяются с его помощью.

самые большие распределения, проходящие через системный распределитель (таким образом, не сообщается в использовании памяти):

                       | PHP 5.6 | PHP 7
interned string buffer | 1 MB    | 150 KB + strings
GC buffer              | 320 KB  | 320 KB
internal classes/funcs | >1.3 MB | >0.5 MB

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

однако это все еще не отвечает на вопрос о фактически сообщенном использовании памяти. В этом случае наибольшие ассигнования:

             | PHP 5.6 | PHP 7
VM stack     | 130 KB  | 256 KB
Object store | 64 KB   | (8 KB)
CG arena     | ---     | 64 KB

здесь есть несколько отличий. Основной из них заключается в том, что PHP 7 использует больший размер страницы VM (примерно в два раза больше). Кроме того, PHP 7 использует арену для хранения определенных структур( например, пользовательских функций), которая начинается с размер по умолчанию 64КБ. С другой стороны, размер буфера хранилища объектов значительно меньше в PHP 7.

таким образом, по сути, ответ TL; DR заключается в том, что PHP 7 использует больший размер страницы стека VM.


Php 5.6 требует меньше байтов по сравнению с Php 7.0.