¿Qué significa Micro Optimización?
Optimizar un código es hacerlo más eficiente, es decir (a grandes rasgos) que se ejecute más rápidamente. Realmente, sea cual sea el lenguaje de programación que utilicemos, el 90% (o incluso más) del rendimiento de nuestro código va a depender de cómo hayamos diseñado la lógica del mismo. Sin embargo, en prácticamente todos los lenguajes de programación existen varias formas de lograr los mismos resultados, y pocos son en los que no hay una que es más eficiente que las demás. El lenguaje PHP, en el que nosotros nos vamos a centrar, no es una excepción.
Así que aunque las mayores mejoras en rendimiento se logran mejorando el diseño de la lógica de una aplicación, sí que existen ciertos detalles a tener en cuenta que, dependiendo el lenguaje que utilicemos, pueden hacer que nuestro código gane un puntito de velocidad de ejecución. Este puntito, aunque sea cuantitativamente minúsculo (incluso quizás algunas meras centésimas de segundo o menos), puede ser crítico cuando un mismo código se ejecuta varios centenares de miles de veces, como puede pasar en un Website con mucho tráfico. Incluso los sitios webs que cachean las páginas que sirven tienen partes críticas de código que deben ser ejecutadas cada vez, o con mucha mayor frecuencia.
The PHP Benchmark
Para evaluar y comparar la eficiencia de varios fragmentos de código, la mejor (y casi única) opción es utilizar un Benchmark. Hace ya algún tiempo, encontré esta página web llamada the PHP Benchmark, que en su momento me pareció curioso pero un poco limitado. Sin embargo, su autor ha seguido ampliándolo con más pruebas hasta lograr resultados más completos y muy interesantes, y hay algunos que quería comentar en este blog ya que estoy seguro que son muy útiles y no tan conocidos.
Aunque he incluido imágenes orientativas con resultados representativos de cada test (ligeramente editadas para que cupieran en el formato de este blog), lo mejor es hacer una visita al propio benchmark, ya que como indica el autor:
«Debes tener en cuenta que hay que refrescar esta página unas pocas veces para «pillar» el resultado correcto. Los números cambian drásticamentes algunas veces con cada refresco. Asumo que esto es por el recolector de basura de PHP, que es invocado aleatoriamiente, y también otros procesos que corren en la máquina pueden tener alguna influencia».
1) Dobles Comillas vs Comillas Simples
En PHP se pueden usar tanto comillas simples ' como comillas dobles " a la hora de definir cadenas de texto (strings, vamos). La principal diferencia entre ambas es que si se incluye una variable dentro de una cadena creada con cadenas dobles, el valor de la variable se expandirá, mientras que con comillas simples no. Los operadores { y } se utilizan para delimitar variables a la hora de expandirlas, sobretodo en el caso de que sean de tipo complejos como objetos o arrays, ya que el parser que busca variables dentro de las cadenas es de tipo greedy, es decir, se para al primer match (más información sobre el parser de PHP). Así,
var $foo= 'soy una variable de tipo string'; '$foo!' == "$foo!"; //false "$foo!" == $foo.'!' == "{$foo}"; //true
Una consecuencia de esta diferencia de uso de las comillas es que, en teoría, es sensiblemente más eficiente para cadenas estáticas el definirlas con comillas simples ', ya que así el parser de PHP no tiene que hacer un preproceso buscando variables dentro de la cadena, sobretodo en el caso de que el texto contenga el maligno caracter $. Por este motivo hace algunos años, en tiempos de PHP 4, se recomendaba a diestro y siniestro el uso de las comillas simples por ser más eficientes. ¿Es realmente así hoy en día?
Según vemos en los resultados del benchmark apenas hay diferencia entre la eficiencia entre el uso de unas comillas y otras. Así podemos quedarnos tranquilos: programando en versiones modernas de PHP, donde se ha mejorado notablemente el rendimiento del parseo de variables dentro de dobles comillas ", podemos elegir la que más nos guste o nos deje un código más legible, factor más que importante sobretodo si juntamos código PHP con etiquetas HTML.
2) Estructuras de Control: if-else vs switch
Cuando aprendemos a programar nos enseñan que si tenemos que comparar entre varias opciones (y el lenguaje de programación lo permite, claro) debemos utilizar una estructura swicth/case/default en vez de encadenar varios if (con sus correspondientes elseif y else). Hasta cierto punto queda más legible (y por tanto más comprensible para terceras personas, y más fácil de mantener) pero ¿va a afectar en algo al rendimiento en PHP?
Podemos comprobar en el benchmark que en PHP las diferencias entre una estructura y otra, aunque existen, no son demasiado relevantes, así que realmente podemos (y debemos) utilizar estructuras switch sin ningún miedo.
Curiosamente existe casi igual diferencia de rendimiento que por variar estructura de control utilizada, por el hecho de utilizar el operador de igualdad == o el operador de identidad === a la hora de comparar los valores. Esto es debido a que el motor de PHP no tiene que realizar ninguna conversión de tipo, por lo que siempre que podamos nos conviene utilizar el operador de identidad ===.
3) Iterando un Array: For vs While vs Foreach
Creo que una de las tareas más comunes en programación es el iterar por los valores de un array. Para esto, necesitamos un bucle, y el bucle «por defecto» en programación es sin duda el humilde for. Las otras opciones con las que la vamos a comparar en PHP son un foreach y un while. Voy a distinguir dos casos, ya que, como vamos a ver a continuación, hay sustanciales diferencias entre un acceso de sólo lectura y uno con modificación de los valores almacenados en el array.
Sólo lectura: En este caso vamos a iterar los elementos de un array sólo accediendo a sus valores, sin modificarlos, con diferentes versiones de los bucles antes mencionados.
Éste es uno de los resultados que más me ha llamado la atención, ya que el más eficiente es un bucle foreach y con bastante diferencia (el for a pelo es casi un 400% más lento, bestial). Además el foreach tiene la ventaja añadida de proporcionarnos un código más limpio, con las ventajas que esto conlleva, ya comentadas anteriormente (legibilidad, mantenibilidad, etc).
Acceso y modificación de los valores:
Sin embargo, a la hora de acceder y modificar los valores de un array de PHP, nos encontramos con que el bucle foreach puede ser, en palabras del autor del benchmark, functionally murderous y el que nos ofrece una mejor eficiencia el el bucle for.
Llama la atención el hecho de que en ninguno de los dos casos el bucle while obtiene buenos resultados de eficiencia, variando entre realmente desastroso para la lectura de los valores, y malo pero aceptable (mejor que el foreach) para la modificación. Malas noticias para los fanáticos de este bucle, si los hay (yo personalmente creo que no lo he utilizado jamás).
4) Varias Iteraciones sobre un Array: La Función Reset
reset($array); //reseteo el puntero interno $key = array_keys($array); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $array[$key[$i]]; //accedo al array
Aunque en el benchmark no se especifica de manera explícita ni se muestran datos comparativos (uno de los pocos fallos que le encuentro), sí se menciona que es mucho más eficiente si utilizamos la poco conocida función reset a la hora de hacer varias iteraciones del array comenzando desde el principio, para «resituar» el puntero interno al array sobre su valor inicial.
5) Precálculo del Tamaño de un Array: Sizeof / Count
//sin precálculo for ($i=0; $i<sizeOf($array); $i++); //con precálculo $size= count($array); for ($i=0; $i<$size; $i++);
Y he dejado la más fácil para el final: absolutamente sí, siempre, debemos precalcular el tamaño de un array de PHP a la hora de iterarlo, utilizando de manera indistinta la función count o su alias sizeof. Si no lo hacemos, un gatito será asesinado de forma horrible.
Muchas gracias por este trabajo, realmente útil!
Lo tendré en cuenta.
Saludos!
Gracias por la información me sacaste de algunas dudas sigue adelante
Muchas gracias.
Buen aporte, muchas gracias!
Gracias!.
Pero una cosa no me queda nada clara… Es peor usar un foreach que un for tras medir la size?. Al fin y al cabo, en foreach cuando se acaban los valores, se acaban los valores y punto ¿no?, mientras de la otra forma te obliga a conocer el tamaño.
Gracias por aportar tu concepto 😉
[…] 5 Consejos de microptimización de PHP […]