Archive for the 'PHP' Category

Declarado el futuro de PHP4

Monday, April 28th, 2008

Para los que pensaban que Zend se iba a echar para atrás; pues no; no solamente fue removido el soporte para PHP4 desde el 1 de enero de este año; Zend ha declarado el “end of life” (Fin de vida) de PHP4 e indica que PHP 4.4.8 (el actual release) será el último a ser lanzado por Zend de esta versión de PHP.

Adicionalmente ha declarado que antes del 8 de agosto del presente año (8/8/2008) será removido complemente los fuentes de PHP 4 del servidor de php.net y sus respectivos mirrors.

Así que señores, a migrar sus aplicaciones! …

Viva Go PHP5!

Go PHP5!

¿Podemos tener PHP5 corriendo en un apache2 worker?

Saturday, April 12th, 2008

La respuesta básica a esta pregunta es sí; la razón de por qué no es tan simple, pero trataré de explicarlo en el siguiente artículo.

Apache 2 con mod_php puede ser ejecutado en dos modos; mpm_prefork y mpm_worker; mpm_prefork es una especie de compatibilidad con apache 1.x y es bueno para aplicaciones que poseen módulos que no son thread-safe; mpm_worker es un sistema multi-proceso multi-threaded (multi-hilado) y requiere que las aplicaciones en él ejecutadas posean hilos seguros de ejecución (thread-safe); mpm_worker es muchisimo más estable y puede ofrecer más conexiones con menos consumo de recursos; además de ser el ideal para equipos multi-core o multi-procesador.

mpm_worker tiene un rendimiento notablemente superior a mpm_prefork en la gran mayoría de las condiciones generales de un servidor web: ver aquí.

Entonces; ¿Por qué se usa mpm_prefork para PHP5? …

Muchas extensiones php5 (y php4) no son thread-safe; esto hace que php5 sea imposible de usar en modo mpm_worker sin “castrarlo” retirando aquellas extensiones que no lo son; sin embargo, esto no es problema alguno, puesto que la gran mayoría de las extensiones no “thread-safe” son básicamente no usadas por la mayoría; un ejemplo es la extensión mm; que permite crear una caché compartida para gestionar las sesiones (y que puede seguramente ser reemplazada por memcached) casi nunca es usada; gd versión 1 debe ser evitada y usar gd2.

Otro mito extendido por esto es que php5 no puede ser ejecutado en modo worker; esto es porque en Debian solo están los binarios compilados en modo prefork y de hecho, instalar php5 desinstala apache2 mpm_worker e instala mpm_prefork; por lo que he decidido compilar mi propio php5 para Debian Lenny (apache 2.2.4 y php 5.2.5) con soporte mpm_worker.

Compilando php5

Primero, debemos instalar algunas librerías que son requeridas (devel) para compilar php5:

>aptitude install libmhash-dev libmhash2 libtidy-dev libgd2-xpm-dev libgd2-xpm libmcrypt-dev libmcrypt4 mcrypt t1lib-bin libt1-dev libxml2-dev libcurl3-dev libaspell-dev libpspell-dev libxslt-dev libbz2-dev libsqlite-dev libdb4.4 db4.4-util libdb4.4++-dev libsqlite3-dev libsnmp-dev

Y luego de descomprimir php-5.2.5.tar.bz2 (descargado de la página de php.net) ejecutamos el siguiente ./configure en la raíz del código fuente:

CFLAGS=”-O3″ CXXFLAGS=”-O3 -fPIC -mtune=nocona -march=nocona \
-fPIC -pipe -fomit-frame-pointer -msse -fexceptions -ffast-math -mfpmath=sse,387″ \
./configure \
–prefix=/usr/php –sysconfdir=/etc/php5/apache2 –mandir=/usr/share/man \
–with-apxs2=/usr/sbin/apxs –libexecdir=/usr/lib/php5 –bindir=/usr/bin –libdir=/usr/lib/php5 \
–with-config-file-path=/etc/php5 –with-config-file-scan-dir=/etc/php5/conf.d \
–with-exec-dir=/usr/lib/php5/libexec –enable-inline-optimization \
–disable-debug –with-curl –with-curlwrappers –with-db4 \
–with-zlib=/usr –enable-bcmath –enable-calendar –enable-mbstring –enable-dba \
–with-libxml-dir=/usr –with-xmlrpc=shared –with-pear=/usr/share/php –with-regex \
–with-pcre-regex=/usr –enable-exif –with-t1lib –with-xsl \
–with-mhash –with-mcrypt –enable-zend-multibyte –with-pspell –enable-zip –enable-bcmath \
–with-jpeg-dir=/usr –enable-soap –enable-sockets –with-xpm-dir=/usr –with-freetype-dir=/usr/lib \
–enable-ftp –with-gd –with-png-dir=/usr –with-ttf \
–enable-json –with-mime-magic –with-tidy=/usr –with-ldap –with-xmlrpc \
–enable-gd-native-ttf –enable-gd-jis-conv –with-gettext –with-xmlrpc –with-xsl \
–with-mysql –with-mysqli \
–enable-sysvmsg –enable-sysvsem –enable-sysvshm –enable-sigchild \
–with-ldap –enable-mbstring –with-bz2 –with-iconv –with-gettext –enable-shmop \
–enable-sockets –enable-wddx –with-zlib \
–with-kerberos=/usr –with-openssl=/usr –enable-soap –with-snmp=/usr \
–with-pgsql –with-sqlite=/usr \
–enable-pdo –without-pdo-dblib –with-pdo-mysql \
–with-pdo-pgsql –with-pdo-sqlite=/usr

ejecutar:

make all

y luego:

make test

permitirá compilar y luego probar nuestro php5 en modo mpm_worker; al final un:

make install

permitirá instalar php5.2.5

Explicando brevemente las sentencias de pre-procesador:

Como verán, antes del .configure hemos ejecutado unas sentencias que mejoran el rendimiento del ejecutable php; entre ellas compilarla para el procesador que actualmente tenemos; en mi caso -mtune=nocona -march=nocona; nocona indica un procesador de doble nucleo (Core2-Duo), existen distintos modos dependiendo del ejecutable; pentium4, pentium-m, athlon-xp, prescott; siempre es bueno leer sobre -mtune y -march en el wiki de gentoo y en la guía de Compilación optimizada de Gentoo.

Apache Benchmark en un php5 con mpm_worker:

Para la ejecución de una aplicación PHP ab reporta:

ab -c 500 -n 500 http://localhost/tomates/

Requests per second: 32.66 [#/sec] (mean)

Conclusiones:

Espero que la utilización correcta de las extensiones de php; la utilización de un php thread-safe en un apache2 mpm_worker permitan a los desarrolladores crear aplicaciones más óptimas y a los servidores proveer una versión más rápida de php5 ejecutado sobre apache.

Notas finales:

La sentencia de compilación está configurada para el enviroment de debian o fedora (/etc/php5 como ruta del archivo php.ini); recuerden revisar bien el –prefix, –libdir, –bindir y otras rutas dependiendo de la distro donde deseen compilar el php5 en modo worker.

xdebug y PHP5 - Haciendo profiling de aplicaciones

Saturday, April 5th, 2008

Hacer profiling de aplicaciones consiste en hacer un mapa (trace) de todas las ejecuciones realizadas por nuestra aplicación con el respectivo tiempo (de CPU, en milisegundos, consumo de RAM, etc) consumido para determinar posibles mejoras en el código, cuellos de botella (bottlenecks) etc.

Quien pensaba que esto era solo una característica de lenguajes como C (que tienen a Valgrind) entonces conozcan la característica de Profiling de Xdebug.

Para instalar Xdebug existen numerosas guías y artículos, me centraré en la posibilidad de realizar el profile de una aplicación como tal.

Configurando xdebug

Para la activación de xdebug he creado un archivo (dentro del directorio conf.d de php5) llamado xdebug.ini que contendrá las instrucciones para activar la extensión y activar de una vez el profiling.

como root:

>touch /etc/php5/conf.d/xdebug.ini

luego, agregamos:

>vim /etc/php5/conf.d/xdebug.ini

zend_extension_ts=”/usr/lib/php5/extensions/no-debug-zts-20060613/xdebug.so”
[xdebug]
xdebug.profiler_enable=1
xdebug.profiler_output_dir=/tmp/php5/profiler

donde fijense que la ruta es donde phpize envía mis extensiones (en este caso, xdebug.so)

Agrego adicionalmente una sección xdebug (se agradece mantener ordenado los archivos ini) y activar “profiler_enable” (1 lo activa, 0 lo desactiva) e indicar la ruta (especificada por profiler_output_dir) donde se ha de crear el archivo de profiling.

guardamos:

:wq!

y reiniciamos nuestro apache:

>/etc/init.d/apache2 restart

En este momento, todas las aplicaciones que ejecutemos de php en nuestro servidor local, generarán un perfil de ejecución en la ruta indicada en profiler.output_dir

Ejecutando la aplicación

Ejecutar la aplicación es simplemente … … “ejecutar la aplicación!” … ya está, sin magia ni trucos …

dentro del directorio indicado se han creado unos archivos, cada uno con un timestamp de ejecución:

cachegrind.out.4286  cachegrind.out.4287  cachegrind.out.4288

Cada archivo es un archivo de texto, conteniendo el perfil de ejecución de la aplicación; ahora bien, ¿con qué lo vemos de una manera más “entendible”? …

Instalando y ejecutando Kcachegrind 

Bueno, el archivo es compatible con los archivos generados por valgrind; así que instalaremos una aplicación para KDE llamada Kcachegrind; simplemente:

>aptitude install kcachegrind

y  luego ejecuten la aplicación (kcachegrind)

Estudiando el perfil

kcachegrind consta de una ventana de 3 paneles:

pantallazo.png

El panel de la izquierda muestra las distintas funciones ejecutadas, desde el comienzo hasta el final, indicando en qué archivo se encontraban, cuando del porcentaje total de ejecución se han consumido y cuanto tiempo en milisegundos les ha tomado en ejecutarse; el panel superior derecho muestra las líneas de código que representa la función seleccionada en el panel derecho y la parte inferior derecha muestra un mapa como este:

profiler.png

Que representa todo el camino (ciclo) de ejecución del código desde donde estamos hasta el final de la ejecución de la clase (objeto), función o archivo seleccionado.

Detectando bottlenecks (cuellos de botella)

Detectar cuellos de botella es fácil de esta manera, simplemente buscamos los mayores tiempos de ejecución y revisamos bloque a bloque hasta encontrar la línea (o líneas) de código que representan la mayor cantidad de tiempo consumido.

bloque.png

De la siguiente ventana de bloque de código deducimos que splib_tomates::init (una función estática de la clase splib_tomates) tiene una duración de 41.10 ms en la línea 57 (call to splib_config::init en el archivo splib_config.php); para no tener que buscar ese archivo en la lista de profiler; simplemente con hacer doble click sobre la linea seleccionada se abrirá el bloque de código (función, archivo) que representa ese bloque de código.

En revisión posterior; determiné que el exceso de tiempo ocurría porque estaba llamando a una librería de caché (de terceros) que estaba ejecutando varios NOTICE y WARNING (aunque Tomates removía los errores); al retirar la librería, se pudo reducir ese tiempo a 26ms.

Conclusiones

No en el 10, el 20 o el 50; casi en el 100% de las aplicaciones web en PHP; los errores y lentitud de ejecución son fáciles de determinar y corregir si todos los desarrolladores antes de liberar una aplicación, ejecutaran debugging y profiling de su código más frecuentemente, realizaran y liberaran pruebas y mantuvieran a raya los “bottlenecks”; pero ya sabemos que eso no ocurre muy a menudo así que queda de parte de los nuevos desarrolladores aprender este tipo de técnicas para mejorar en el desarrollo de sus aplicaciones.