web-dev-qa-db-fra.com

Je suis confronté à plus de consommation de mémoire dans Php 7 par rapport à PHP 5.6

Quand je faisais un benchmark, j'ai trouvé que PHP 7 utilisait plus de mémoire que PHP 5.6.

J'ai donc fait un test. J'ai exécuté un script contenant uniquement:

  $a=10;

et ci-dessous sont les résultats de la mémoire utilisée lorsque j'ai utilisé PHP CLI sans aucun module (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

L'environnement est

  • OS: fenêtre 10
  • Serveur: IIS (bien que j'aie utilisé la CLI, pas le serveur), avec cgi rapide
  • machine: 64 bits
  • php-5.6.23-nts-Win32-VC11-x64
  • php-7.0.9-nts-Win32-VC14-x64

Quelqu'un peut-il expliquer pourquoi j'ai obtenu ce résultat?


Tests supplémentaires

En utilisant ce code, comme suggéré par @gordon,

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

php 5.6: 227408 octets

php 7.0: 386640 octets

J'ai déterminé l'utilisation de la mémoire avec ce code:

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();
19
developerCK

Pour comprendre la réponse à votre question - vous devez comprendre comment PHP5 et PHP7 allouent la mémoire.

PHP5 allouer de la mémoire "By Request" en supposant par sa structure Zend Engine.

Dans PHP7 ce sont des optimisations faites de ce côté, donc l'allocation de mémoire "par les morceaux"

  • Au démarrage, il alloue un gros morceau de mémoire
  • Sur l'allocation dans l'application, il alloue de petits morceaux pour éviter la fragmentation

Cette différence améliore considérablement les performances (car le moteur n'a pas besoin d'allouer de la mémoire à l'exécution à chaque fois que vous en avez besoin et économise du temps sur la fragmentation), mais il augmente la consommation de mémoire pour les "très petits" programmes, dont la taille est inférieure à "taille de morceau".

Et oui, PHP7 économise beaucoup de mémoire sur les gros programmes.

Vous pouvez voir toutes ces différences dans les images ci-dessous:

PHP memory allocation for large programsPHP memory allocation for small programs

Graphiques construits avec 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]+)$/\1,/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]+)$/\1,/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]+)$/\1,/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]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m

tiroir d'octave

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");

Lire la suite

  1. Présentation officielle de PHP7/PHP-NG: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
  2. Description officielle des changements internes PHP7/PHP-NG: https://wiki.php.net/phpng-int
  3. Guide officiel de migration des extensions: https://wiki.php.net/phpng-upgrading
  4. Bons articles de @NikiC: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html et http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2
  5. Détails internes PHP5: http://www.phpinternalsbook.com/
  6. Histoire de réussite de Badoo PHP5-> PHP7 avec des détails: https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/
22
MobDev

Vos tests montrent plus d'utilisation de la mémoire dans PHP 7.0 car le code de test est très simple.

PHP 7.0 est connu pour utiliser moins de mémoire (et être plus rapide) que PHP 5.6 en raison d'une réécriture radicale du moteur ZEND interne (le noyau de l'interpréteur)

Comme Gordon a très probablement commenté les nouvelles fonctionnalités et améliorations de PHP 7.0 nécessite un "bootstrap" qui donne des résultats négatifs lors du test sur de petits morceaux de code.

Essayons-le avec quelque chose de plus complexe: construisez un tableau de 10 000 entiers puis triez-le en utilisant Quicksort algorythm.

Voici le résultat que j'obtiens:

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

Et un simple tri rapide de 20 lignes est loin des applications du monde réel avec des milliers de lignes de codes, de nombreuses déclarations de classes, de nombreuses instances ...

J'ai exécuté le test sur http://phptester.net

Voici le code

<?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;

Crédit pour l'algorithme quicksort en PHP: http://andrewbaxter.net/quicksort.php

13
Paolo

Au départ, je tiens à dire que si vous voyez une utilisation de la mémoire signalée plus élevée dans PHP 7 sur du code réel, la cause la plus probable est que PHP 7 signalera l'utilisation de la mémoire des requêtes tamponnées mysqlnd dans le cadre de l'utilisation de la mémoire . Dans PHP 5, cette utilisation de la mémoire n'était pas signalée (mais bien sûr, la mémoire était toujours utilisée). Pour les requêtes volumineuses, cela peut faire une différence très importante.

Passons maintenant à votre cas réel, qui est essentiellement l'utilisation de la mémoire de PHP immédiatement après le démarrage de la demande. Le réponse de MobDev explique déjà pourquoi il existe une différence dans l'utilisation de la mémoire "réelle", qui est la métrique d'utilisation de la mémoire qui indique la quantité de mémoire que l'allocateur PHP a demandée à l'allocateur système du noyau. Comme MobDev le fait remarquer, PHP 7 allouera de la mémoire dans des blocs beaucoup plus grands (2 Mo) et est également plus agressif dans la mise en cache des blocs alloués.

Toutefois, cela n'explique pas l'écart dans l'utilisation de la mémoire "non réelle", qui ne prend pas en compte ces détails d'allocateur. Il est facile de vérifier si la mémoire passe exactement en utilisant un profileur de mémoire, par ex. en exécutant PHP via USE_ZEND_ALLOC=0 valgrind --tool=massif. La partie USE_ZEND_ALLOC=0 Indique à PHP de ne pas utiliser son propre allocateur.

Tout d'abord, cela vous montrera que l'utilisation réelle de la mémoire et l'utilisation de la mémoire rapportée par PHP diffèrent de manière assez significative. Massif affichera 3,2 Mo pour PHP 5,6 et 2,3 Mo pour PHP 7. La raison en est que PHP ne signale que la mémoire qui passe par son propre allocateur (ZMM), tandis que de nombreuses structures qui survivre à travers plusieurs requêtes ne sont pas allouées en l'utilisant.

Les allocations les plus importantes passant par l'allocateur système (donc non signalées dans l'utilisation de la mémoire) sont:

                       | 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

Le nombre de "classes/fonctions internes" est une borne inférieure brute, car il y a beaucoup de petites allocations impliquées ici qui sont difficiles à compter. Une différence principale est visible, qui est que PHP 7 n'utilise pas de tampon de chaîne interne fixe (la taille indiquée est celle du tampon de table de hachage que je vois, qui n'inclut pas la taille des chaînes elles-mêmes).

Cependant, cela ne répond toujours pas à la question de l'utilisation de la mémoire réellement signalée. Dans ce cas, les allocations les plus importantes sont:

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

Il y a quelques différences ici. Le principal est que PHP 7 utilise une taille de page VM plus grande (environ deux fois plus grande). De plus, PHP 7 utilise une arène pour stocker certaines structures (comme les fonctions utilisateur), qui commence avec une taille par défaut de 64 Ko. D'un autre côté, la taille du tampon de stockage d'objets est nettement plus petite dans PHP 7.

Donc, essentiellement, la réponse TL; DR est que PHP 7 utilise une plus grande taille de page de pile VM.

9
NikiC