php php-internals

Me enfrento a un mayor consumo de memoria en Php 7 en comparación con PHP 5.6



php-internals (4)

Cuando estaba haciendo un punto de referencia, descubrí que PHP 7 estaba usando más memoria que PHP 5.6.

Entonces, hice una prueba. He ejecutado un script que contiene sólo:

$a=10;

y a continuación se muestran los resultados de la memoria utilizada cuando usé PHP CLI sin ningún módulo ( 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

El medio ambiente es

  • OS: ventana 10
  • Servidor: IIS (aunque utilicé la CLI, no el servidor), con cgi rápido
  • máquina: 64 bit
  • php-5.6.23-nts-Win32-VC11-x64
  • php-7.0.9-nts-Win32-VC14-x64

¿Alguien puede explicar por qué obtuve este resultado?

Exámenes adicionales

Usando este código, como lo sugiere @gordon,

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

php 5.6: 227408 bytes

php 7.0: 386640 bytes

Determiné el uso de memoria con este código:

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


Para comprender la respuesta a su pregunta, debe comprender cómo PHP5 y PHP7 asignan memoria.

PHP5 asignando memoria "Por solicitud" asumiendo por su estructura Zend Engine.

En PHP7 son algunas optimizaciones hechas en este lado, así que en la asignación de memoria "por los trozos"

  • Al inicio asigna gran parte de la memoria.
  • En la asignación dentro de la aplicación, asigna una pequeña parte para evitar la fragmentación.

Estas diferencias hacen que el rendimiento aumente muy bien (debido a que el motor no necesita asignar memoria en tiempo de ejecución cada vez que lo necesita y ahorra tiempo en la fragmentación), pero aumenta el consumo de memoria para programas "muy pequeños", cuyo tamaño es inferior a "tamaño de porción".

Y sí, PHP7 ahorra mucho la memoria en grandes programas.

Puedes ver todas estas diferencias en las imágenes a continuación:

Gráficos construidos con 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;

banco.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

cajón de octava

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

Lee mas

  1. Presentación oficial de PHP7 / PHP-NG: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
  2. Descripción oficial de los cambios internos de PHP7 / PHP-NG: https://wiki.php.net/phpng-int
  3. Guía oficial de migración de extensiones: https://wiki.php.net/phpng-upgrading
  4. Buenos artículos de @NikiC: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html y http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2
  5. Detalles internos de PHP5: http://www.phpinternalsbook.com/
  6. Badoo PHP5-> Historia de éxito de PHP7 con detalles: https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/

Php 5.6 requiere menos bytes en comparación con Php 7.0.


Por adelantado, quiero decir que si ve un mayor uso de memoria reportado en PHP 7 en código real, la causa más probable es que PHP 7 informará sobre el uso de la memoria de las consultas con búfer de mysqlnd como parte del uso de la memoria. En PHP 5 este uso de memoria no se informó (pero, por supuesto, la memoria todavía se usaba). Para consultas grandes esto puede hacer una diferencia muy importante.

Ahora a su caso real, que es básicamente el uso de la memoria de PHP inmediatamente después del inicio de la solicitud. La respuesta de MobDev ya explica por qué hay una discrepancia en el uso de memoria "real", que es la métrica de uso de memoria que informa de cuánta memoria ha solicitado el asignador de PHP al asignador de sistema del kernel. Como señala MobDev, PHP 7 asignará memoria en trozos mucho más grandes (2 MB) y también es más agresivo en el almacenamiento en caché de los trozos asignados.

Sin embargo, esto no explica la discrepancia en el uso de memoria "no real", que no tiene en cuenta estos detalles del asignador. Es fácil comprobar si exactamente la memoria se va utilizando un generador de perfiles de memoria, por ejemplo, ejecutando PHP a través de USE_ZEND_ALLOC=0 valgrind --tool=massif . La parte USE_ZEND_ALLOC=0 indica a PHP que no use su propio asignador.

En primer lugar, esto le mostrará que el uso de memoria real y el uso de memoria reportado por PHP difieren bastante significativamente. Massif mostrará el uso de 3.2MB para PHP 5.6 y 2.3MB para PHP 7. La razón es que PHP solo reporta la memoria que pasa por su propio asignador (ZMM), mientras que muchas estructuras que sobreviven en múltiples solicitudes no se asignan al usarlas.

Las asignaciones más grandes que pasan por el asignador del sistema (por lo tanto, no se informan en el uso de la memoria) son:

| 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

El número de "clases / funciones internas" es un límite inferior bruto, porque aquí hay muchas asignaciones pequeñas que son difíciles de contar. Una diferencia principal es visible, que es que PHP 7 no usa un búfer interno de cadena fijo (el tamaño indicado es el del búfer de tabla hash que estoy viendo, que no incluye el tamaño de las propias cadenas).

Sin embargo, esto todavía no responde a la pregunta del uso de memoria realmente informado. En este caso las mayores asignaciones son:

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

Hay un par de diferencias aquí. La principal es que PHP 7 usa un tamaño de página de VM más grande (aproximadamente el doble de grande). Además, PHP 7 usa una arena para almacenar ciertas estructuras (como las funciones de usuario), que comienza con un tamaño predeterminado de 64 KB. Por otro lado, el tamaño del búfer del almacén de objetos es significativamente menor en PHP 7.

Básicamente, la respuesta de TL; DR es que PHP 7 usa un tamaño de página de pila de VM más grande.


Sus pruebas muestran más uso de memoria en PHP 7.0 porque el código de prueba es muy simple.

Se sabe que PHP 7.0 usa menos memoria (y es más rápido) que PHP 5.6 debido a una reescritura radical del motor interno de ZEND (el núcleo del intérprete)

Como Gordon comentó, lo más probable es que las nuevas características y mejoras en PHP 7.0 requieran un "bootstrap" que como resultado resultados negativos cuando se prueba en pequeñas piezas de código.

Intentémoslo con algo más complejo: cree una matriz de 10.000 enteros y ordénelos utilizando el algoritmo de orden rápida.

Aquí está el resultado que obtengo:

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

Y aún así, un simple quicksort de 20 líneas está muy lejos de las aplicaciones del mundo real con miles de líneas de códigos, muchas declaraciones de clases, muchos casos ...

He ejecutado la prueba en http://phptester.net

A continuación se muestra el código

<?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édito por el algoritmo de ordenación rápida en PHP: http://andrewbaxter.net/quicksort.php