Pregunta

¿Qué tipo de implicaciones de rendimiento hay que considerar al utilizar declaraciones try-catch en PHP 5?

He leído información antigua y aparentemente contradictoria sobre este tema en la web antes.Gran parte del marco con el que tengo que trabajar actualmente se creó en PHP 4 y carece de muchas de las sutilezas de PHP 5.Por lo tanto, yo no tengo mucha experiencia en el uso de try-catchs con php.

¿Fue útil?

Solución

Una cosa a considerar es que el costo de un bloque de prueba donde no se lanza ninguna excepción es una cuestión diferente del costo de lanzar y detectar una excepción.

Si las excepciones sólo se lanzan en casos de falla, es casi seguro que no le importará el rendimiento, ya que no fallará muchas veces por ejecución de su programa.Si estás fallando en un circuito cerrado (también conocido como:golpearse la cabeza contra una pared de ladrillos), es probable que su aplicación tenga peores problemas que ser lenta.Así que no se preocupe por el costo de lanzar una excepción a menos que de alguna manera se vea obligado a usarla para el flujo de control regular.

Alguien publicó una respuesta hablando sobre el código de creación de perfiles que genera una excepción.Nunca lo he probado yo mismo, pero predigo con confianza que esto mostrará un impacto en el rendimiento mucho mayor que simplemente entrar y salir de un bloque de prueba sin lanzar nada.

Otra cosa a considerar es que cuando anidas llamadas a muchos niveles de profundidad, incluso puede ser más rápido tener un solo intento... capturar justo en la parte superior que verificar los valores de retorno y propagar errores en cada llamada.

En lo contrario de esa situación, donde descubres que estás envolviendo cada llamada en su propio bloque try...catch, tu código será más lento.Y más feo.

Otros consejos

Estaba aburrido y perfilé lo siguiente (dejé el código de tiempo):

function no_except($a, $b) { 
    $a += $b;
    return $a;
}
function except($a, $b) { 
    try {
        $a += $b;
    } catch (Exception $e) {}
    return $a;
}

usando dos bucles diferentes:

echo 'no except with no surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
    no_except(5, 7);
}
echo 'no except with surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
    try {
        no_except(5, 7);
    } catch (Exception $e) {}
}
echo 'except with no surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
    except(5, 7);
}
echo 'except with surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
    try {
        except(5, 7);
    } catch (Exception $e) {}
}

Con 1000000 ejecuciones en mi cuadro WinXP, ejecute Apache y PHP 5.2.6:

no except with no surrounding try = 3.3296
no except with surrounding try = 3.4246
except with no surrounding try = 3.2548
except with surrounding try = 3.2913

Estos resultados fueron consistentes y se mantuvieron en proporciones similares sin importar el orden en que se realizaron las pruebas.

Conclusión:Agregar código para manejar excepciones raras no es más lento que codificar las excepciones que ignoran.

Los bloques try-catch no son un problema de rendimiento: el verdadero cuello de botella en el rendimiento proviene de la creación de objetos de excepción.

Código de prueba:

function shuffle_assoc($array) { 
    $keys = array_keys($array);
    shuffle($keys);
    return array_merge(array_flip($keys), $array);
}

$c_e = new Exception('n');

function no_try($a, $b) { 
    $a = new stdclass;
    return $a;
}
function no_except($a, $b) { 
    try {
        $a = new Exception('k');
    } catch (Exception $e) {
        return $a + $b;
    }
    return $a;
}
function except($a, $b) { 
    try {
        throw new Exception('k');
    } catch (Exception $e) {
        return $a + $b;
    }
    return $a;
}
function constant_except($a, $b) {
    global $c_e;
    try {
        throw $c_e;
    } catch (Exception $e) {
        return $a + $b;
    }
    return $a;
}

$tests = array(
    'no try with no surrounding try'=>function() {
        no_try(5, 7);
    },
    'no try with surrounding try'=>function() {
        try {
            no_try(5, 7);
        } catch (Exception $e) {}
    },
    'no except with no surrounding try'=>function() {
        no_except(5, 7);
    },
    'no except with surrounding try'=>function() {
        try {
            no_except(5, 7);
        } catch (Exception $e) {}
    },
    'except with no surrounding try'=>function() {
        except(5, 7);
    },
    'except with surrounding try'=>function() {
        try {
            except(5, 7);
        } catch (Exception $e) {}
    },
    'constant except with no surrounding try'=>function() {
        constant_except(5, 7);
    },
    'constant except with surrounding try'=>function() {
        try {
            constant_except(5, 7);
        } catch (Exception $e) {}
    },
);
$tests = shuffle_assoc($tests);

foreach($tests as $k=>$f) {
    echo $k;
    $start = microtime(true);
    for ($i = 0; $i < 1000000; ++$i) {
        $f();
    }
    echo ' = '.number_format((microtime(true) - $start), 4)."<br>\n";
}

Resultados:

no try with no surrounding try = 0.5130
no try with surrounding try = 0.5665
no except with no surrounding try = 3.6469
no except with surrounding try = 3.6979
except with no surrounding try = 3.8729
except with surrounding try = 3.8978
constant except with no surrounding try = 0.5741
constant except with surrounding try = 0.6234

Generalmente, use una excepción para protegerse contra fallas inesperadas y use la verificación de errores en su código contra fallas que son parte del estado normal del programa.Para ilustrar:

  1. Registro no encontrado en la base de datos: estado válido, debe verificar los resultados de la consulta y enviar mensajes al usuario de manera adecuada.

  2. Error de SQL al intentar recuperar el registro: error inesperado, el registro puede estar allí o no, pero tiene un error de programa; este es un buen lugar para una excepción: registre el error en el registro de errores, envíe un correo electrónico al administrador con el seguimiento de la pila y muestre un mensaje de error cortés para el usuario informándole que algo salió mal y que estás trabajando en ello.

Las excepciones son costosas, pero a menos que manejes todo el flujo de tu programa usándolas, cualquier diferencia de rendimiento no debería ser perceptible para los humanos.

No encontré nada sobre el rendimiento de Try/Catch en Google, pero una prueba simple con un error de lanzamiento de bucle en lugar de una declaración IF produce 329 ms frente a 6 ms en un bucle de 5000.

Lamento publicar en un mensaje muy antiguo, pero leí los comentarios y no estoy de acuerdo, la diferencia puede ser mínima con códigos simples, o puede ser insignificante cuando se usan Try/Catch para partes específicas del código que no son siempre predecible, pero también creo (no probado) que un simple:

if(isset($var) && is_array($var)){
    foreach($var as $k=>$v){
         $var[$k] = $v+1;
    }
}

es más rápido que

try{
    foreach($var as $k=>$v){
        $var[$k] = $v+1;
    }
}catch(Exception($e)){
}

También creo (no probado) que a:

<?php
//beginning code
try{
    //some more code
    foreach($var as $k=>$v){
        $var[$k] = $v+1;
    }
    //more code
}catch(Exception($e)){
}
//output everything
?>

es más caro que tener IF adicionales en el código

¡Esa es una muy buena pregunta!

Lo he probado muchas veces y nunca vi ningún problema de rendimiento ;-) Era cierto hace 10 años en C++ pero creo que hoy lo han mejorado mucho porque es muy útil y limpio.

Pero todavía tengo miedo de rodear mi primer punto de entrada con esto:

try {Controller::run();}catch(...)

No probé con muchas funciones de llamada y gran inclusión...¿Alguien ya lo ha probado completamente?

En términos generales, son caros y no valen la pena en PHP.

Dado que es un lenguaje de expresiones verificadas, DEBE detectar cualquier cosa que genere una excepción.

Cuando se trata de código heredado que no arroja y código nuevo que sí lo hace, solo genera confusión.

¡Buena suerte!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top