fight(), Midiendo que código dura menos en PHP
Ultimamente he estado intentando hacer pruebas de velocidad para comprobar como podía hacer mas rápidos mis códigos, o que variaciones podían mejorar su consumo de memoria y me enfrente a las primeras mediciones, las cuales en la mayoria de casos eran como tirar una moneda al aire. De manera que me decidí a crear una función que me ayudara a comparar 2 script dándome resultados aproximadamente fiables y ayudando a realizar las decisiones de que código tarda menos en tiempo de proceso y memoria consumida.
El problema real de hacer estas mediciones es que cuando no se tiene un servidor dedicado, hacer estos controles es muy poco fiable al poder estar consumiendo muchos recursos otros dominios vecinos dentro del servidor que interfieren en las mediciones que realizamos, de manera que empece a hacer pruebas con las funciones microtime() y memory_get_usage().
Al realizar las mediciones tomando una muestra de tiempo o de memoria antes y después de un código, y restando sus valores para determinar cuanto ha tardado en realizarse o cuanto ha consumido, los resultados eran en ocasiones negativos o demasiado grandes de manera que me decidí a utilizar una forma empírica para determinar esos valores. Realizando una medición nula, una segunda de muestra con valores que deberían dar tiempos de 0ns y 0bts de memoria determinaba una base a partir de la cual cuantificar los resultados, pero como aun así los test eran realmente contradictorios decidí establecer un protocolo de repetición. Si una vez realizadas 2 pruebas los datos eran contradictorios o confusos se realizaba otra tanda mas… siendo el mínimo 6 y el máximo 150, siempre hablando de las pruebas de tiempo transcurrido en procesar los códigos. el numero medio que suele necesitar como base para determinar cual de los dos datos es mas rápido suele ser 30 repeticiones del código.
Las mediciones de memoria a pesar de mostrar un fallo en la primera medición suelen ser estables, dando siempre o casi siempre un valor estable de datos. Para evitar errores se realiza una medición nula, una base para determinar el valor de error de consumo de memoria y una ultima con los códigos a medir a los que se les resta la medición base. De esta manera con una sola pasada obtenemos los mejores resultados con menos código.
Entrando en materia, para mantener la simetría de tiempos de acceso y que todos los códigos sean lo más parecidos en tiempos de acceso, opté por hacer algunas funciones de apoyo a la función fight(). De esta manera tenemos:
microtime_float() que normaliza el numero obtenido de microtime() para poder operar con el de forma normal.time_eval() y mem_eval() que devuelven el valor de cuanto tiempo tarda el código introducido en ser ejecutado y cuanta memoria consume respectivamente, time_empiric.mem_empiric() que ralizan las mediciones haciendo las comprobaciones de tiempo y memoria respectivamente en base a las experiencias anteriores.A partir de las anteriores, fight() realiza una serie de repeticiones que intentan asegurar un buen resultado con un indice de fiabilidad lo mas alto posible, comparando los resultados conjuntos anteriores.
Código de las funciones necesarias:
-
function microtime_float(){
-
return ((float)$useg + (float)$seg);
-
}
-
-
function time_eval($code){
-
$tiempo_inicio = microtime_float();
-
$tiempo_final = microtime_float();
-
return ($tiempo_final - $tiempo_inicio);
-
}
-
-
function time_empiric($code1, $code2, $debug = false){
-
#referencia de proceso nulo para deducción empirica
-
$ref1 = $ref2 = '/**/';
-
#TEST 1 - 2 ################################################
-
$nul1 = time_eval($ref1); $nul2 = time_eval($ref2);
-
$A2 = time_eval($code2); $A1 = time_eval($code1);
-
#TEST 3 - 4 ################################################
-
$nul3 = time_eval($ref2); $nul4 = time_eval($ref1);
-
$B1 = time_eval($code1); $B2 = time_eval($code2);
-
#TEST 5 - 6 ################################################
-
$nul5 = time_eval($ref1); $nul6 = time_eval($ref2);
-
$C1 = time_eval($code1); $C2 = time_eval($code2);
-
#TEST 7 - 8 ################################################
-
$nul7 = time_eval($ref2); $nul8 = time_eval($ref1);
-
$D2 = time_eval($code2); $D1 = time_eval($code1);
-
#ERROR#CODE##############
-
#########################
-
#########
-
$AB1 = ($A1<$B1) ? 1 : 0; $AC1 = ($A1<$C1) ? 1 : 0; $AD1 = ($A1<$D1) ? 1 : 0;
-
$AB2 = ($A2<$B2) ? 1 : 0; $AC2 = ($A2<$C2) ? 1 : 0; $AD2 = ($A2<$D2) ? 1 : 0;
-
$BC1 = ($B1<$C1) ? 1 : 0; $BD1 = ($B1<$D1) ? 1 : 0; $BC2 = ($B2<$C2) ? 1 : 0; $BD2 = ($B2<$D2) ? 1 : 0;
-
$CD1 = ($C1<$D1) ? 1 : 0; $CD2 = ($C2<$D2) ? 1 : 0;
-
$_1 = $AB1+$AC1+$AD1 + $BC1+$BD1 + $CD1;
-
$_2 = $AB2+$AC2+$AD2 + $BC2+$BD2 + $CD2;
-
$OUTS = ($_1<$_2) ? $code1: $code2;
-
-
if($debug){
-
$media1 = ($A1+$B1+$C1+$D1)/4;
-
$media2 = ($A2+$B2+$C2+$D2)/4;
-
} else {
-
return $OUTS;
-
}
-
}
-
-
function mem_eval($code){
-
$mem_inicio = $mem_final = 0;
-
return ($mem_final - $mem_inicio);
-
}
-
-
function mem_empiric($code_uno, $code_dos, $debug = false){
-
#limpiado y calibrado de las variables
-
$nul1 = mem_eval('//');
-
$nul2 = mem_eval('/**/');
-
-
#muestreo de emenplo base de memoria en texto
-
$nul1 = mem_eval('//');
-
$nul2 = mem_eval('/**/');
-
-
#realizamos la comparacion
-
$uno = mem_eval($code_uno);
-
$dos = mem_eval($code_dos);
-
-
$OUTS = ($uno<$dos) ? $code_uno: $code_dos;
-
if($debug){
-
} else {
-
return $OUTS;
-
}
-
}
-
-
function fight($code_uno, $code_dos, $debug = false){
-
$s1 = time_empiric($code_dos, $code_uno, true);
-
$s2 = time_empiric($code_uno, $code_dos, true);
-
-
$out = $s1['debug']['tiempos'][$s1['ganador']]."\n".$s2['debug']['tiempos'][$s2['ganador']]."\n";
-
$s[time_empiric($code_dos, $code_uno)] += 1;
-
$s[time_empiric($code_uno, $code_dos)] += 1;
-
}
-
-
$i = 1;
-
foreach($s as $k => $v){
-
//$out .= $k.' = '.$s[$k]."%\n";
-
$fiabilidad[$i] = ($s[$k]>59) ? true : false;
-
$i++;
-
}
-
-
if(!($fiabilidad[1] || $fiabilidad[2]) || $veces>150){
-
$o = fight($code_dos, $code_uno);
-
$veces += $o['time']['base'];
-
$t[$code_dos] += $o['time'][$code_dos]; $t[$code_dos] = ($t[$code_dos]/2);
-
$t[$code_uno] += $o['time'][$code_uno]; $t[$code_uno] = ($t[$code_uno]/2);
-
}
-
$_m = mem_empiric($code_uno, $code_dos, true);
-
-
$t['base'] = $veces;
-
$t['ganador'] = ($t[$code_uno]<$t[$code_dos]) ? $code_uno: $code_dos;
-
$m = array('base'=> '1', $code_uno=> $_m['consumo'][$code_uno], $code_dos=> $_m['consumo'][$code_dos], 'ganador'=> $_m['ganador']);
-
$m[] = '1';
-
$m[$code_uno] = $_m['consumo'][$code_uno];
-
$m[$code_dos] = $_m['consumo'][$code_dos];
-
$m['ganador'] = $_m['ganador'];
-
-
if($debug){
-
}
-
if($o['base']>150){
-
$s['veredicto'] = 'fiable.'; //
-
} elseif($s[$code_dos]>=75 || $s[$code_uno]>=75){
-
$s['veredicto'] = 'muy fiable. // Mucha diferencia'; //
-
} else {
-
$s['veredicto'] = 'poco fiable. // Resultados muy igualados // Poca diferencia'; //
-
}
-
return $s;
-
}
Para utilizarlo es muy sencillo. Insertamos en el primer valor el primer código a testar y en el segundo, el segundo códigos a testar procurando que en estos códigos no se contengan funciones. Ejemplo:
-
$code_uno = '$uno = md5(100);';
-
$code_dos = '$uno = base64_encode(100);';
-
El resultado de la función será un array con las key time, mem y veredicto. En los dos primeros casos podremos ver un array que indica el numero de repeticiones (base), el codigo que menos ha tardado o menos memoria ha consumido respectivamente (ganador) y los códigos testados con sus datos de prioridad. En el valor "veredicto" se indica de una forma textual la fiabilidad del test.
Si se inserta true en la 3º opcion a demas se insertara en time y mem un valor "debug" en el que se insertarán los valores de los 2 primeros test. Ejemplo:
-
$code_uno = '$uno = md5(100);';
-
$code_dos = '$uno = base64_encode(100);';
-
Comparte este artículo
Licencia de los contenidos
© Todos los derechos reservados