-
Notifications
You must be signed in to change notification settings - Fork 0
12 rendimiento en php
Hay dos factores fundamentales a la hora de mejorar el rendimiento de los scripts en PHP: reducir el uso de memoria y reducir el tiempo de carga. Existen diferentes formas para mejorarlos, vamos a ver algunas:
| 1. Garbage collector | 5. Desplazar tareas a un segundo plano |
| 2. OPCache | 6. Aprovechar el HTTP caching |
| 3. Optimizar las sesiones | 7. Perfilar el código |
| 4. Optimizar la base de datos |
El mecanismo garbage collector tiene un impacto tanto en el uso de memoria como en el tiempo de ejecución.
- Reducción en el uso de memoria con Garbage Collector
El objetivo del recolector de basuras es reducir el uso de memoria limpiando las variables de referencias circulares. En PHP esto ocurre cuando el root buffer está lleno o cuando se llama a la función gc_collect_cycles(). El siguiente es un ejemplo con diferentes usos de memoria en función de las variables creadas:
- FASE 1: PHP consume un mínimo de memoria al principio, aunque no se haga nada:
echo "Fase 1, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE : 248,79KB- FASE 2: Se crea un loop con un array de 10.000 de valores de números aleatorios:
$array = array();
for ($i = 0; $i < 10000; ++$i){
$array[] = rand();
}
echo "Fase 2, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 1705.32 KB. Gran aumento por la cantidad de elementos del array- FASE 3: Se crean 2 variables:
$var1 = 1;
$var2 = 2;
echo "Fase 3, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 1705.6 KB. Se produce un ligero aumento por los dos nuevos zval- FASE 4: Se asignan los símbolos de las variables anteriores (var1,var2) a $array:
$var1 = $array;
$var2 = $array;
echo "Fase 4, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 1705.51 KB. El uso de memoria es prácticamente el mismo, var1 y var2
// tan sólo son símbolos del mismo zval. Esto es gracias a la copia en escritura- FASE 5: Se asigna el símbolo array a un array vacío:
$array = array();
echo "Fase 5, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 1705.64 KB. Aumenta un poco el uso de memoria por el nuevo array (nuevo zval)- FASE 6: Se añade un valor al array de var2:
$var2[] = "Hola!";
echo "Fase 6, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 2693.33 KB. Aquí se produce un aumento notable. PHP hace de nuevo una copia en escritura, var2 hace una copia del array que apuntaba antes y añade el nuevo elementoEn este momento, var1 y var2 apuntan a arrays diferentes y $array apunta a un array vacío
- FASE 7: Se crea un array vacío en el símbolo de var1
$var1 = array();
echo "Fase 7, el uso de memoria es de: ", round(memory_get_usage()/1024, 2), "KB";
// DEVUELVE 1705.91 KB. Notable disminución. A var1 se le asigna el valor de un array vacío, cuando antes tenía un array muy grande. Aqui es donde se produce una limpiezaAunque a var2 ya no se le hace referencia en ningún momento, no se añade a la colección de basuras. PHP lo guarda en memoria hasta que finaliza el script.
¿Qué conclusiones se sacan de éste ejemplo?
-
Si quieres que una variable en ámbito global libere su memoria, utiliza la función unset() o asígnale un valor diferente. Sino, PHP continuará guardándola en memoria hasta el final.
-
La copia en escritura es muy útil, y los arrays se copian por referencia de la misma forma que lo hacen los objetos. Pero hay que tener en cuenta que un cambio en el array hace una copia en la memoria.
-
En el momento en que haces unset() o reasignas una variable, PHP libera su memoria. Liberar memoria no es gratis (especialmente con grandes cantidades) en términos de tiempo de procesamiento, lo que significa que si quieres que tu script se ejecute lo más rápido posible a costa de gastar RAM, deberías evitar usar garbage collector en grandes variables, y dejar que PHP lo haga sólo al final del script.
Veamos ahora un ejemplo con la creación de una clase:
class Clase
{
public $var = '3.141592';
}
$baseMemory = memory_get_usage();
for ( $i = 0; $i <= 1000000; $i++ )
{
$x = new Clase;
$x->self = $x;
if ( $i % 500 === 0 )
{
echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "<br>";
}
}En este ejemplo se crea un objeto en el que una propiedad enlaza de nuevo al mismo objeto. Cuando $x se reasigna en la siguiente iteración del loop, antes de PHP 5.3 se produciría una fuga de memoria.
Después de 10.000 iteraciones, el buffer se llena y lanza el mecanismo recolector de basura y libera memoria asociada con esas posibles raíces.
- Aumento en el tiempo de ejecución con Garbage Collector
El garbage collector también influye en el tiempo de ejecución por el tiempo que lleva a éste a liberar la memoria fugada. Si partimos del siguiente script:
$start = microtime(true);
class Clase
{
public $var = '3.14159265359';
}
for ( $i = 0; $i <= 100000; $i++ )
{
$x = new Clase;
$x->self = $x;
}
echo round(memory_get_peak_usage()/1024, 2), "<br>";
$end = microtime(true);
$time = number_format(($end - $start), 2);
echo $time;Y lo ejecutamos una primera vez con garbage collector habilitado y en otra deshabilitado, obtenemos los siguientes tiempos:
Tiempo con GC habilitado: 0,23 Tiempo con GC deshabilitado: 0,17
La diferencia de tiempo con este script es de sólo 0,06 segundos, en cambio el uso de memoria ha sido:
Memoria usada con GC: 5815.88 KB Memoria usada sin GC: 54470.23 KB
La diferencia en el consumo de memoria es enorme.
En general el recolector de basuras en PHP sólo provoca un retraso cuando el algoritmo recolector de ciclos se activa (más de 10.000 roots), sino no hay impacto en el rendimiento.
OPCache mejora el rendimiento de PHP almacenando el código de bytes de un script precompilando en la memoria compartida, eliminando la necesidad de que PHP cargue y analice los scripts en cada petición.
Cada script PHP se compila en tiempo de ejecución, parte de ese tiempo se dedica a traducir el código legible para los humanos en código que la máquina puede entender. Un motor de cache bytecode como OPCache, APC o Xcache lo hace sólo una vez durante la primera ejecución de un archivo PHP. Después el script precompilado se guarda en la memoria, lo que aumentará notablemente el rendimiento en una aplicación PHP.
La implementación de OPCache permite recibir más requests por segundo y devolver respuestas más rápido. Este artículo plantea un ejemplo de un e-commerce construído en Symfony2 donde analiza más profundamente el impacto de OPCache en el código. El artículo concluye con un 14% de reducción en el tiempo de respuesta en general, pero hay otros análisis que concluyen una mejora muy superior, como el de fideloper.
-
APC cache no funciona correctamente en las últimas versiones de PHP, resultando en errores de ejecución.
-
OPCache está ligado (bundled) a PHP a partir de su versión 5.5, y también puede ser instalado en versiones anteriores.
-
OPCache era conocido anteriormente como Zend Optimizer+, hasta que Zend decidió hacerlo open source e incluirlo en el núcleo de PHP.
La conclusión es que se debería usar OPCache, y si se está usando APC cache en un proyecto, lo ideal sería migrarlo a OPCache.
Si ya tienes instalado y configurado OPCache puede ser importante controlar qué archivos PHP están siendo cacheados. El motor de la cache funciona en segundo plano pero es transparente y se puede comprobar su estado, principalmente mediante las funciones opcache_get_configuration() y opcache_get_status().
También existen scripts disponibles que emplean la configuración y los datos de estado y los muestra de forma más amigable, como opcache-status, de Rasmus Lerdorf o el OPCacheGUI.
En cada proyecto hay que asegurarse que opcache.use_cwd está establecido como true. Activando esta configuración OPCache buscará en todos los directorios para diferenciar entre archivos con los mismos nombres. Si se establece como false, puede conllevar a colisiones entre archivos con el mismo nombre base.
En frameworks que usan anotaciones, como Doctrine o PHP Unit, hay que establecer opcache.load_comments y opcache.save_comments a true. Así los comentarios de los archivos también en incluyen en el código precompilado de OPCache.
Se pueden excluir ciertos archivos de ser cacheados, como los que tienen datos de configuración. Es necesario activar la opción opcache.blacklist_filename con el directorio donde está la lista que archivos que quieres excluir.
Para desactivar OPCache en entornos de desarrollo o testing, se suele mantener globalmente OPCache con _opcache.enabl_e en el archivo de configuración php.ini, y si no se quiere usar la cache en algún entorno o proyecto, se desactiva con la función ini_set().
Aunque HTTP es un protocolo sin estado, la mayoría de aplicaciones web requieren una forma de manejar los datos de usuarios. En PHP se controlan mediante sesiones.
La mejor opción para guardar las sesiones es mediante una base de datos y emplear una cache LRU (Least Recently Used) como Memcached o Redis. Lo ideal es limitar el tamaño de datos de sesión a unos 4096 bytes y guardar todos los datos de sesión en una cookie encriptada.
Normalmente las aplicaciones hacen uso de las bases de datos para organizar el contenido. Dependiendo de la cantidad de datos y como se accede a ellos, puede costar muchos recursos y tiempo hacer peticiones. Una forma fácil de solventarlo es utilizar una cache como Memcached o Redis que permita guardar peticiones e invalidarlas y obtener peticiones actualizadas cuando éstas quedan obsoletas.
Una buena forma de hacer todo esto automáticamente es empleando un mapa de objetos relacional ORM como Doctrine, que tiene soporte incorporado para Memcached y Redis.
Hay ciertas tareas en un script que pueden llevar cierto tiempo. En muchos casos no es necesario que el usuario tenga que esperar a que ciertas tareas se completen, por ejemplo cuando se rellena un formulario y es necesario enviar un email de respuesta al usuario.
La solución es apartar el código del flujo principal y ponerlo en una cola o sistema de mensajes, para que luego se maneje mediante un sistema paralelo. Dos notables beneficios derivan de este sistema: experiencia de usuario y escalar el trabajo de procesos largos añadiéndolos a la cola.
Existen diferentes herramientas que permiten realizar estas tareas, algunas de las más populares son php-resque, RabbitMQ y ActiveMQ.
Es conveniente leer la especificación de la caché de HTTP, si se usa correctamente puede ahorrar mucha memoria. Este es un tutorial muy bueno (y bastante largo) sobre la cache web. El libro HTTP: The Definitive Guide también tiene una sección muy interesante dedicada al tema.
Para proyectos y aplicaciones con alto tráfico, es muy recomendable emplear una cache proxy como Varnish para aliviar la carga de los servidores.
Lo mejor es aplicar el mayor número de métodos para mejorar el rendimiento, en la medida de lo posible.
Perfilar el código permite recopilar estadísticas de ejecución de un script, como ver el nombre de las funciones ejecutadas, número de veces que se han ejecutado, etc. Analizar estos datos nos permite ver qué se puede mejorar en nuestro código.
Xdebug es una extensión PHP para depurar el código. Permite seguir el recorrido del script y recopilar información de perfil y de memoria.
Existen aplicaciones frontend para Xdebug como WebGrind, y también es fácilmente adaptable a IDEs como PHPStorm.
| Anterior | Siguiente |
@intelguasoft - Full Stack Developer
| Versión: | 0.0.3-beta |
El curso está organizado según las temáticas indicadas en la página oficial de Rogue Wave Zend para la certificación de PHP.
Esta curso no tiene ninguna relación con Zend. |
|---|---|---|
| Fecha: | 28.11.2018 |
Básicos
Sintaxis básicaOperadores
Operadores bit a bit
Variables
Estructuras de control
Constantes y constructores base
Espacio de nombres
Extensiones
Configuraciones
Variables al descubierto
Recolector de basuras
Rendimiento (Performance)
Funciones
FuncionesArgumentos en funciones
Funciones variables
Valores por referencia en funciones
Funciones que devuelven algo
Ámbito de variables
Funciones anónimas y closure's
Cadenas y patrones
Las comillas y las cadenas de caracteresHeredoc y Nowdoc
Comparando cadenas de caracteres
Extracción en cadenas de caracteres
Análisis en cadenas de caracteres
Reemplazos en cadenas de caracteres
Formato de cadena de caracteres
Expresiones regulares (RegEx)
Codificación de caracteres
Codificación en cadenas de caracteres
Arreglos (Array's)
ArreglosArreglos asociativos
Iterar arreglos
Funciones de arreglos
SPL Arreglos mediante objetos
Conversión de arreglos
Archivos (I/O)
Manejo de archivosLectura de archivos
Escritura de archivos
Funciones del sistema de archivos
Socket's y archivos
Streams (?)
Seguridad
Concepto y funcionamiento CGIConfigurando la seguridad
Seguridad en Sesiones
Ataques XSS
Ataques CSRF
Ataques SQLInjection
Ataques CodeInjection
Ataques EmailInjection
Filtrado de datos de entrada
Escape de datos de salida
Encriptación y contraseñas
Seguridad en el almacenamiento de datos
Seguridad en la subida de datos
SSL y OpenSSL
Base de datos
Principios básicos SQLLos joins en SQL
Principales funciones SQL
Preparando sentencias SQL
Transacciones en SQL
Algo de PDO
Programación Orientada a Objetos
Instancias de clasesModificadores y herencia de clases
Interfaces
Excepciones
Auto-carga (Autoload)
Reflexión (Reflection)
Determinación de tipos (Type hinting)
Constantes de clase
Enlace estático de ejecución (Late Static Binding)
Métodos mágicos
Librería estándar PHP (SPL)
Generadores (Generators)
Traits
Clases abstractas
Formatos de información
Algo de XMLAlgo de SimpleXML
Algo de XML Parser
Algo de PHP DOM
Algo de Web Services
Web Services con SOAP
Algo de REST
Algo de JSON
Formato de fecha y hora
Características web's
SesionesFormularios
Métodos GET y POST
Cookies
Protocolo HTTP y sus headers
Autenticación HTTP
Códigos de estado HTTP