web-dev-qa-db-fra.com

Performances de try-catch en php

Quel type d'implications de performance faut-il considérer lors de l'utilisation des instructions try-catch dans php 5?

J'ai déjà lu sur le Web des informations anciennes et apparemment contradictoires à ce sujet. Une grande partie du framework avec lequel je travaille actuellement a été créée sur php 4 et n'a pas beaucoup de subtilités de php 5. Donc, je n'ai pas beaucoup d'expérience moi-même dans l'utilisation de try-catchs avec php.

58
Travis

Une chose à considérer est que le coût d'un bloc d'essai où aucune exception n'est levée est une question différente du coût de lancer et d'attraper une exception.

Si des exceptions ne sont levées qu'en cas d'échec, vous ne vous souciez presque pas des performances, car vous n'échouerez pas très souvent par exécution de votre programme. Si vous échouez dans une boucle serrée (par exemple: vous cogner la tête contre un mur de briques), votre application a probablement de plus gros problèmes que d'être lente. Ne vous inquiétez donc pas du coût de lever une exception, sauf si vous êtes en quelque sorte obligé de les utiliser pour un flux de contrôle régulier.

Quelqu'un a publié une réponse parlant du code de profilage, ce qui lève une exception. Je ne l'ai jamais testé moi-même, mais je prédis avec confiance que cela montrera une performance bien plus élevée que de simplement entrer et sortir d'un bloc d'essai sans lancer quoi que ce soit.

Une autre chose à considérer est que lorsque vous imbriquez des appels à de nombreux niveaux, il peut même être plus rapide d'avoir un seul essai ... attraper tout en haut que pour vérifier les valeurs de retour et propager des erreurs à chaque appel.

À l'opposé de cette situation, où vous trouvez que vous encapsulez chaque appel dans son propre bloc try ... catch, votre code sera plus lent. Et plus laid.

67
Steve Jessop

Je m'ennuyais et je profilais ce qui suit (j'ai laissé le code de chronométrage):

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

en utilisant deux boucles différentes:

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) {}
}

Avec 1000000 s'exécute sur ma boîte WinXP exécutez Apache et 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

Ces résultats étaient cohérents et sont restés dans une proportion similaire quel que soit l'ordre des tests.

Conclusion: l'ajout de code pour gérer les exceptions rares n'est pas plus lent que le code qui ignore les exceptions.

52
jmucchiello

Les blocs try-catch ne sont pas un problème de performances - le véritable goulot d'étranglement des performances provient de la création d'objets d'exception.

Code de test:

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";
}

Résultats:

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
19
Brilliand

En règle générale, utilisez une exception pour vous prémunir contre les échecs inattendus et utilisez la vérification des erreurs dans votre code contre les échecs qui font partie de l'état normal du programme. Pour illustrer:

  1. Enregistrement introuvable dans la base de données - état valide, vous devez vérifier les résultats de la requête et envoyer un message approprié à l'utilisateur.

  2. Erreur SQL lors de la tentative de récupération de l'enregistrement - échec inattendu, l'enregistrement peut ou peut ne pas être là, mais vous avez une erreur de programme - c'est un bon endroit pour une exception - enregistrez l'erreur dans le journal des erreurs, envoyez à l'administrateur la trace de la pile et affichez-la un message d'erreur poli à l'utilisateur l'informant que quelque chose s'est mal passé et que vous y travaillez.

Les exceptions sont coûteuses, mais à moins que vous ne gériez l'intégralité de votre flux de programme en les utilisant, aucune différence de performances ne doit être perceptible par l'homme.

8
Aeon

Je n'ai rien trouvé sur les performances Try/Catch sur Google mais un simple test avec une erreur de lancement de boucle au lieu d'une instruction IF produit 329 ms contre 6 ms dans une boucle de 5000.

5
Patrick Desjardins

Désolé de poster un message très ancien, mais j'ai lu les commentaires et je suis en désaccord, la différence peut être minime avec un simple morceau de code, ou elle peut être négligeable lorsque le Try/Catch est utilisé pour des parties spécifiques du code qui ne le sont pas toujours prévisible, mais je crois aussi (non testé) qu'un simple:

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

est plus rapide que

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

Je crois également (non testé) que:

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

est plus cher que d'avoir des FI supplémentaires dans le code

2
Fabrizio

C'est une très bonne question!

Je l'ai testé plusieurs fois et je n'ai jamais vu de problème de performance ;-) C'était vrai il y a 10 ans en C++ mais je pense qu'aujourd'hui ils l'ont beaucoup amélioré depuis qu'il est si utile et plus propre.

Mais j'ai toujours peur d'en entourer mon premier point d'entrée:

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

Je n'ai pas testé avec beaucoup d'appels de fonctions et de gros inclus .... Est-ce que quelqu'un l'a déjà testé complètement?

1
Tom