phpLa optimización de código es algo que ha preocupado a los programadores desde hace mucho tiempo, aunque por suerte o por desgracia, esta preocupación está decrementando día a día dado que los ordenadores con cada vez más potentes y por lo tanto importa menos el malgaste de ciclos de CPU.

Esta preocupación por el tiempo de ejecución de un programa viene dado sobre todo por el desarrollo de proyectos grandes (muchas visitas simultáneas) en los que sea crucial la velocidad de ejecución del algoritmo, por otro lado para aquellos proyectos que dada su escalabilidad pudiesen aumentar su complejidad de una forma un tanto brusca, es importante que el núcleo sea tan eficiente como sea posible además de, obviamente, tener el mínimo número de errores como sea posible, y por último puede que se dé el caso que en el caso de que tengas un servidor compartido, tu hosting te haya dado un toque porque haces uso de demasiados ciclos de CPU a la vez que te instan a migrarte a un plan de hosting más avanzado y que por consiguiente posea más recursos para tu script.

En este artículo vamos a intentar dar algunos consejos que permitan mejorar el rendimiento de nuestras aplicaciones web escritas en PHP con relativamente poco esfuerzo.

1.- Versión de PHP

Si estás utilizando una versión antigua de PHP será conveniente que hicieses una actualización de la misma en tu servidor, esto es así porque con cada versión, el equipo desarrollador de PHP ha ido implementando nuevas funciones así como optimizando partes de código, llegando a, reducir notablemente el tiempo de ejecución y la memoria consumida por determinados scrips y funciones de las que vienen por defecto.

A día de hoy, las últimas versiones estables de PHP son la 5.3.0 y la 5.2.11, eso sí, en ningún caso recomiendo instalar en un servidor en producción una versión que no sea estable.

2.- Utilizar algún sistema de Cacheado

En lenguajes interpretados, como es PHP, cada vez que se desea ejecutar un script, éste ha de ser interpretado, lo que hace que el consumo de CPU se incremente más que si se tratase de un lenguaje compilado, además de que ha de ejecutar todas las operaciones y funciones cada vez que se ejecute, lo que hace que si éstas son complejas, el script se ralentice considerablemente; un ejemplo de estas funciones son las llamadas a la base de datos.

A pesar de que no es recomendable cachear todas las páginas en algunos proyectos, en ocasiones es bastante recomendable utilizar aplicaciones como Memcache para así evitar que se ejecute el script siempre que se hace una petición de una página.

Otra opción es hacer uso de sistemas de templates, como es Smarty, que hace un cacheado de la página pero ejecutando siempre las consultas a la base de datos.

3.- Optimizar el código

3.1.- Strings

En PHP las cadenas de caracteres (Strings) pueden, al contrario que en lenguajes como C o Java, ir rodeados, tanto de comillas dobles () como de comillas simples (), pero a pesar de que el resultado pueda llegar a ser el mismo, el comportamiento de una cadena delimitada por uno u otro de estos caracteres se comporta de manera diferente.

En PHP las cadenas de caracteres delimitadas por comillas simples requieren un consumo inferior de ciclos de CPU, esto es así, porque las cadenas encerradas por comillas dobles, analizan el string carácter a carácter en búsqueda de variables en PHP para sustituirlas por su valor, gracias a lo cual se evita tener que estar concatenando cadenas con variables, además si quieres imprimir un símbolo del dólar, tendrás que escaparlo($).

En conclusión, aunque nos suponga un poco más de trabajo el hecho de andar concatenando cadenas de caracteres, merece la pena en cuanto a rendimiento.

Ejemplo:

<?php

$m = 'mundo';

echo "hola $m !";

?>
<?php

$m = 'mundo';

echo hola ' . $m . ' !'; // hola mundo!

?>

Como podrás comprobar la salida de ambos código es hola mundo! pero la segunda forma de mostrar el mensaje es más eficiente que la primera, como hemos comentado anteriormente.

3.2.- Impresión de caracteres

Siempre que sea posible intenta imprimir todo aquello que no sea una variable y pueda imprimirse “tal cual” como código HTML, de manera que así evitamos que PHP analice ese área de texto y por lo tanto simplemente lo coloque en el buffer de salida, a su vez si hemos de imprimir algo que no es posible hacerlo con código HTML, entonces hemos de elegir cuidadosamente como hacer esto.

Hay dos funciones muy utilizadas para imprimir textos por pantalla, las cuales son echo y print, siendo la pirmera de estas la más rápida en realizar la misma tarea, además si deseamos imprimir varios textos, uno detrás de otro, si hacemos uso echo, podemos evitar la operación de concatenación (cancatenar es una función muy costosa) ya que echo admite varios parámetros.

Ejemplos:

<?php

$m = 'mundo';

$ex = '!';

?>

Hola <?php echo $m . $ex;?>

Hola <?php print($m . $ex);?>

Hola <?php echo $m, $ex;?>

Por otro lado, si deseamos evaluar lo sucedido dentro de la función es conveniente hacer uso de print(), ya que esta última devuelve un valor indicando cómo ha ido la operación.

3.3.- Reducir las peticiones SQL

Trabajar con una base de datos como por ejemplo MySQL en nuestras aplicaciones web programadas en PHP es bastante común, ya que esto nos permite interactuar con los usuarios de nuestra página así como llevar estadísticas actualizadas al momento entre otros.

Pero no todo lo que reluce es oro, a cambio de esta gran versatilidad que nos da combinar PHP con un SGBD (sistema de gestión de bases de datos) castiga fuertemente la rapidez de ejecución del script, por lo que minimizar lo máximo posible el número de consultas a la base de datos puede ser un hecho crucial, para hacer eso, podemos:

  • Cruzar tablas en la propia sentencia SQL.
  • Hacer JOINS entre distintas tablas antes que ejecutar más de una query para obtener todos los datos necesarios.
  • A la hora de insertar más de un elemento en la base de datos, evitar hacerlo dentro de un bucle o ejecutando secuencialmente varias.
<?php

// opción menos óptima

foreach ($table as $item) {
mysql_query('INSERT INTO A (aa,ab) VALUES("' . $item['aa'] . '", "' . $item['ab'] . '")');
 }

// opción más óptima

$data = array();
 foreach ($table as $item) {
$ata[] = '("' . $item['aa'] . '", "' . $item['ab'] . '")';
 }
 $query = 'INSERT INTO A (aa,ab) VALUES' . implode(',', $data);
 mysql_query($query);

?>

El contenido de la variable $query de la opción óptima será:


INSERT INTO A (aa, ab) VALUES (aa1, ab1), (aa2, ab2), ... , (aan, abn)

3.4.- Usar las funciones de PHP

Siempre que sea posible haz uso de las funciones que ya están implementadas en PHP en lugar de implementarlas tu mismo, ya que las implementadas en PHP son funciones muy testeadas y además muy optimizadas, además, ¿para qué calentarnos la cabeza en hacer algo que ya está hecho y además de uso gratuito?

Eso sí, aun así sigue siendo más eficaz hacer uso de constantes predefinidas que llamar a funciones que calculen su valor aunque estas últimas estén implementadas en el propio motor de PHP, como por ejemplo sería el caso de la función pi().

3.5.- Elimina los dato no necesarios

PHP tiene un recolector de basura (garbage collector) propio, el cual va liberando automáticamente la memoria inutilizada para evitar hacer un gasto innecesario de memoria, pero los algoritmos de estos “recogedores de basura” son complicados y no son ni mucho menos perfectos, así que si le ayudamos un poco y además así maximizaremos la memoria disponible en el momento deseado.

Para liberar estos recursos podemos hacer uso de la variable unset($variable) para así liberar la memoria.

3.6.- split() vs. explode()

A pesar de que split() es capaz de reconocer expresiones regulares y partir una cadena de caracteres utilizando uno de estos patrones, es más lento que explode(), por lo que siempre que sea posible ha de usarse esta última. Además de esto, la función split(), en la versión 5.3 de PHP está en desaprobación (deprecated).

3.7.- Inclusión de archivos

Las funciones include(), require(), include_once() y require_once() son funciones muy utilizadas en los proyectos PHP ya que estas nos permiten explotar la modularidad a la hora de programar, pero no todas ellas se comportan de la misma manera (require() devuelve un fatal error mientras que include() devuelve un warning), y es que include_once() y require_once(), antes de hacer la inclusión del código del nuevo fichero comprueba si éste ya ha sido añadido con anterioridad dentro del mismo script, lo que lo hace más lento que include() y require().

Por último en el caso de que el archivo a incluir no tenga porqué ser analizado, podemos hacer uso de la función readfile(), esta función coloca el archivo indicado en el buffer de salida directamente. Ésto es bastante útil si queremos permitir la descarga de un archivo sin que se conozca la URL real del archivo que estamos descargando.

Ejemplo:

obtenido de php.net

<?php
$file = 'monkey.gif';

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}
?>

3.8.- Incrementos

Una de las operaciones matemáticas más utilizadas en cualquier lenguaje de programación es la de incrementar, ya que ésta es utilizada en muchos de los bucles que se ejecutan en el programa.

Hay dos tipos de incrementos:

  • $i++
  • ++$i

Ambas operaciones incrementan en una unidad el valor de la variable ($i en nuestro caso), la única diferencia es que $i++ incrementa después de devolver el valor de $i, mientras que ++$i incrementa antes de devolver el valor de $i, pues bien, la operación de pre-incremento (++$i) consume un opcode menos que la de post-incremento ($i++);

Ejemplo:

<?php

$i = 0;

echo $i;  // 0

echo ++$i; // 1

echo $i++; // 1

echo ++$i; // 3

?>

Como último consejo para este apartado, también es más aconsejable hacer uso de ++$i que $i += 1;

3.9.- Bucles

Los bucles son un arma de doble filo, por un lado nos facilitan mucho la vida permitiéndonos ejecutar una o varias acciones de forma repetida, pero por otro lado, los bucles pueden medrar en la optimicidad del código, por ejemplo el hecho anidar bucles, hace que el número de veces que se ejecutará el código del interno venga determinado por el número de vueltas que da el bucle externo por el número que da el interno, lo que de nuevo puede disparar el consumo de ciclos de CPU y de memoria, ya analizaremos en otro post como medir la complejidad de un algoritmo.

A pesar de que al usar un bucle puede convertirse en la peor de nuestras pesadillas (no suele ser así) podemos mejorar el rendimiento de los mismos levemente eligiendo correctamente qué tipo de bucle utilizar, a continuación listaré los tipos de bucles ordenados de menor a mayor coste:

  • do{ … }while( … )
  • while( … ) { … }
  • for( … ; … ; … ) { … }

3.10.- Variables estáticas

Hacer uso de variables estáticas hace que al asignarle el valor de una a otra en lugar de copiarse el contenido de la misma, lo que se copia es una referencia al contenido, es decir que el contenido de la variable sólo está almacenado una vez en memoria, lo que puede llegar a ahorrar una cantidad interesante de memoria.

3.11.- Constantes

Las constantes son utilizadas para valores no variables, ésto hace que el comportamiento de los compiladores (intérprete en este caso), en lugar de meter en memoria el valor como una variable, lo que se hace es sustituir directamente las cadenas que identifican la constante por su valor.

3.12.- Evita la copia de variables

La copia o asignación de variables no estáticas producen una copia del contenido de la una en la otra, lo que hace que se multiplique por n el espacio en memoria utilizado para almacenar en muchas ocasiones la misma información, ya que si bien muchas veces la copia de una variable se realiza con el fin de tener una copia de seguridad, otras muchas se hace simplemente para mejorar la legibilidad del código, un ejemplo muy claro de este último hecho es almacenar el contenido de las variables pasadas de una página a otra mediante el uso de los métodos GET o POST para así tener que escribir menos para utilizarla y a su vez para que la comprehensión del código mejore en la medida de los posible.

3.13.- Comprobación de variables

Comprobar la existencia de una variable es muy útil, por ejemplo se hace uso de esta estrategia con el fin de evitar la impresión de mensajes de Warnings de PHP.

Siempre que queramos comprobar la existencia de una variable, es preferible hacer uso de las funciones en el orden en el que voy a escribirlas a continuación:

Por último decir que es más rápido acceder a una variable es más rápido que comprobar si un elemento de un array existe, por lo que para recorrer un array con un bucle es aconsejable guardar la longitud del mismo en una variable para hacer las comparaciones una vez lleguemos al bucle.

Ejemplo:

<?php

$i = 0;
 $length = count($array);
 while($i < $lenght){
++$i;
 }

?>

3.14.- Supresión de errores

El uso de la “@” antes de una linea que suele lanzar un Error o Warning es una técnica de uso muy habitual, pero ésta es a su vez una medida muy costosa, lo que hace que aunque lleve más trabajo sea más aconsejable hacer uso de la siguiente táctica:

<?php

/*function() es la función a ejecutar mientras que siDaError() representa al conjunto de acciones que se han de han de ejecutar en caso de error*/

function() or siDaError();

?>

3.15.- Operadores

Si ahora nos centramos en los operadores de comparación, podemos distinguir dos grandes grupos:

  • ==, !=, <=, =>
  • ===, !==, <==, ==>

El primer grupo para ver si dos elementos son iguales, simplemente comprueba su valor, razón por la cual si se evalua una condición del tipo:

<?php

if(true == 1){ ... }

?>

El resultado de la comparación sera true, por l que entrará en la sección entre corchetes en lugar de saltarsela y/o irse al else.

El segundo grupo además de comparar los valores también compara los tipos de datos, lo que hace que sea un poco más lento que la primera opción.

3.16.- Alias

Un alias no es más que un segundo nombre que se le da a una función, el uso de éstos es considerablemente más lento, ya que tiene que llamar a la función principal.

En la siguiente lista os mustro algunos de los alias de algunas funciones (en negrita):

  • chop -> rtrim
  • close -> closedir
  • die -> exit
  • dir -> getdir
  • diskfreespace -> disk_free_space
  • fputs -> fwrite
  • ini_alter -> ini_set
  • is_writeable -> is_writable
  • join -> implode
  • pos -> current
  • rewind -> rewinddir
  • strchr -> strstr
  • sizeof -> count

3.17.- If/Else vs. Switch

Es preferible hacer uso de If/Else a utilizar Switch, ya que eon más rápidos.

3.18.- Bufferes

Esto realmente no es un hecho de ahorrar ciclos de CPU, pero un uso correcto de los mismos nos permitirá crear una sensación de velocidad de carga en el usuario final de la página web, ya que podemos forzar la impresión de todo lo que se halle en el buffer hasta el momento, permitiendo así que el usuario sea capaz de visualizar su contenido.

3.19.- Valor y Referencia

Cierto es que ésta no se trata de una característica de PHP, ya que C y otros lenguajes también permiten pasar variables a funciones por valor o por referencia, pero ¿cuál es la diferencia?

Pasar una variable por valor fuerza la creación de una nueva variable con el mismo valor que la del parámetro correspondiente, lo que a su vez es bastante interesante porque nos permite modificar el valor dentro de la función sin que este cambio se vea reflejad en la variable del hilo de ejecución principal.

Por otro lado pasar una variable por referencia consiste en pasarle a la función un puntero a la variable anterior, por así decirlo, no se produce una copia del valor, sino que las dos variables, la de dentro de la función y la de fuera apuntan al mismo area de memoria, lo que nos evita una horro en memoria y en tiempo, el único inconveniente de ésto es que si el valor de la variable se modifica dentro de la función, éste también quedará modificado fuera de la misma.

Ejemplo:

<?php

/*paso por valor*/

function valor($a){

$a++;

}

/* paso por referencia */

function referencia(&$a){

$a++;

}

/* programa */

$a=0; // $a = 0

valor($a); // $a = 0;

referencia($a); // $a = 1;

?>

3.20.- Classes

El hecho de utilizar, acceder o modificar un atributo o un método de una clase es mucho más lento que hacer lo mismo con una función o variable local, por lo que hay que evaluar si es conveniente hacer uso de una clase o por otro lado no es necesario.

4.- Cuellos de Botella

Los cuellos de botella son los puntos más críticos de un script, aquellos puntos en los que se puede generar una cantidad ingente de actividad, lo que además de ralentizarlo cabría la posibiladad de que el consumo de memoria tabién se dispare en mayor o menor medida.

Identificar los cuellos de botella y solventarlos pueden aumentar la eficiencia del código enormemente.

Fuentes: Emezeta, tufuncion, Google

Compartir

0Shares
0 0

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies