8 de julio de 2011

[PHP] Verificación de entradas en arrays multidimensionales (2/2)

conjuntoEntradas() {
  [PHP] Verificación de entradas en arrays multidimensionales (1/2)
  [PHP] Verificación de entradas en arrays multidimensionales (2/2)
}


Llegado al punto que al no ser las dos opciones comentadas en el primer post de esta serie de mi gusto por el motivo que ya expuse, además estar desfavorecidas frente a otras, por su menor rendimiento en velocidad de ejecución, podría haber optado por tirar por el derecho y decidir utilizar la función isset(), es decir la que en el comentario de la documentación de función is_null(), se dejaba claro que era la más óptima entre estas tres opciones.

No obstante, durante mi búsqueda de como hacer la verificación de la existencia de claves dentro de los arrays multidimensionales, me tope con otra función, que por nombre y definición (Verifica si el índice o clave dada existe en el array) debería ser la más apropiada para realizar dicho menester; estoy hablando de la función array_key_exists().

No obstante, en los comentarios de la documentación de array_key_exists(), alguien y había comentado la diferencia de velocidad de ejecución con isset(), siendo de nuevo, favorable para ésta última.

Aún habiendo este comentario, yo quería hacer mis pruebas, ya que al final del comentario, se menciona que la diferencia de rendimiento no es tan grande en un sistema operativo Mac o Linux, como lo es un MS Windows; además la prueba del comentario se realiza sobre un array unidimensional y no multidimensional, que son con los que yo me iría encontrando en mis implementaciones y por otra parte, me entró la vena friki por realizar la prueba sobre un profiler, y como ya había utilizado en una vez anterior, el elegido fue Xdebug, utilizando, con el mismo motivo, el visor KCachegrind; así que manos, la obra.

Primero la implementación de las funciones que me permitirán discernir entre las dos distintas formas de verificar la presencia de claves en arrays multidimensionales con la función array_key_exists().

function aa_verificacion_a_una_instruccion_fallo_2d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave2.5', $var1['clave1.3'])) {
        }
    }
}

function ab_verificacion_por_dimension_fallo_2d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                (array_key_exists('clave2.5', $var1['clave1.3']))) {
        }
    }
}

function ba_verificacion_a_una_instruccion_fallo_3d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1'])) {
        }
    }
}

function bb_verificacion_por_dimension_fallo_3d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']) ) {
        }
    }
}

function ca_verificacion_a_una_instruccion_fallo_4d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}

function cb_verificacion_por_dimension_fallo_4d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave3.0', $var1['clave1.3']['clave2.1']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}


function da_verificacion_a_una_instruccion_fallo_3d_sobre_4d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.1'])) {
        }
    }
}

function db_verificacion_por_dimension_fallo_3d_sobre_4d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave3.1', $var1['clave1.3']['clave2.1']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}
Aunque se podía intuir con total certeza que la verificación a una instrucción, directamente haciendo referencia a la clave en la dimensión que se quiere verificar, será más óptima que la verificación dimensión por dimensión hasta llegar a la profundidad de la dimensión que realmente se quiere comprobar; opté por realizar la prueba y así poder ver de manera cuantificable la diferencia que había, y tener que tragarme mis gustos y optar por hacer la comparación con isset(), haciendo referencia a posiciones de memoria inexistentes si las dimensiones de menor profundidad a al que se quiere verificar, ya no existen, como a comenté en la primera entrada de esta serie. Los resultados obtenidos fueron estos:

 Diagrama de llamadas a funciones de comparación de la dos maneras de utilizar array_key_exists()

Diagrama de llamadas a funciones de comparación de la dos maneras de utilizar array_key_exists() de menos a mayor % utilizado 

Bueno, una vez hecho un poco el ganso, tocaba realizar la comprobación definitiva, comparar la eficiencia de ejecución entre array_key_exists() e isset(), así que esté fue el script que utilicé:
function aa_usando_array_key_exists($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}

function ab_usando_isset($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (isset($var1['clave1.3']['clave2.1']['clave3.0']['clave4.1'])) {
        }
    }
}

function ba_usando_array_key_exists_fallo_dimension_anterior($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.5']['clave3.0'])) {
        }
    }
}

function bb_usando_isset_fallo_dimension_anterior($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (isset($var1['clave1.3']['clave2.1']['clave2.5']['clave4.1'])) {
        }
    }
}
Y los resultados obtenidos fueron estos:


Diagrama de llamadas a funciones de comparación entre la utilización de array_key_exists() e isset()


Diagrama de llamadas a funciones de comparación entre la utilización de array_key_exists() e isset() de menos a mayor % utilizado

Así que como dice el dicho, “una imagen vale más que 1000 palabras”, la eficiencia queda demostrada.

Para concluir solo me queda aclarar un aspecto que alguno le debe descuadrar; se trata de ¿porqué en el caso de la utilización de isset() no tengo ninguna pega por lo que he comentado con el resto? Me refiero al hecho de no gustarme hacer referencias a posiciones de memoria inexistentes. Pues bien, el caso de isset() lo veo sustancialmente distinto, ya que por definición la función sirve para “determinar si una variable esta definida y no es NULL”, así que aparte de considerar que lo que va a hacer es verificar la existencia o inexistencia de una posición de memoria (en términos de definición de posiciones de memoria alocadas en tu programa), si la posición no existe no aparece ningún mensaje de notificación informando de ello, claro está sin anteponer @ a la llamada de la función; así que no me da la sensación de estar realizando algo que no es del todo correcto.


Hasta la próxima enfermos.

No hay comentarios:

Publicar un comentario