66 votes

Performances de try-catch en php

Quelles sont les implications en termes de performances à prendre en compte lors de l'utilisation des instructions try-catch en php 5 ?

J'ai déjà lu sur le web des informations anciennes et apparemment contradictoires sur ce sujet. Une grande partie du framework avec lequel je dois travailler actuellement a été créée sous php 4 et ne dispose pas de toutes les fonctionnalités de php 5. Je n'ai donc pas beaucoup d'expérience dans l'utilisation de try-catchs avec php.

74voto

Steve Jessop Points 166970

Il faut tenir compte du fait 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 la levée et de la capture d'une exception.

Si les exceptions ne sont lancées que dans les cas d'échec, il est presque certain que vous ne vous souciez pas des performances, puisque vous n'échouerez pas très souvent par exécution de votre programme. Si vous échouez dans une boucle serrée (c'est-à-dire si vous vous cognez la tête contre un mur de briques), votre application a probablement des problèmes plus graves que sa lenteur. Ne vous inquiétez donc pas du coût du lancement d'une exception, à moins que vous ne soyez obligé de les utiliser pour le flux de contrôle régulier.

Quelqu'un a posté une réponse concernant le profilage du code qui lève une exception. Je ne l'ai jamais testé moi-même, mais je prédis avec confiance que les performances seront bien plus affectées que si l'on se contente d'entrer et de sortir d'un bloc try sans rien lancer.

Une autre chose à prendre en compte est que lorsque vous imbriquez des appels à plusieurs niveaux de profondeur, il peut même être plus rapide d'avoir un seul try...catch au sommet que de vérifier les valeurs de retour et de propager les erreurs à chaque appel.

Dans le cas contraire, où vous vous apercevez que vous enveloppez chaque appel dans son propre bloc try...catch, votre code sera plus lent. Et plus moche.

0 votes

Je n'ai jamais vu de code où ils enveloppent chaque appel avec un try catch au lieu d'utiliser une seule grande try cacth pour emballer tous les appels !

3 votes

@Marco : ça arrive dans le code qui ne fait pas veulent d'utiliser les exceptions lui-même, mais est obligé d'utiliser une API qui le fait. Ainsi, chaque appel à cette API se retrouve enveloppé, attrape les exceptions et les transforme en codes d'erreur ou autre. Peut-être que cela ne se produit pas vraiment en PHP, mais cela peut être un danger dans d'autres langages lorsque différents styles de codage s'affrontent. L'autre cas de figure est celui où vous voulez traiter les exceptions différemment selon leur origine (enregistrer discrètement certaines exceptions, avertir l'utilisateur pour d'autres, échouer pour d'autres encore) plutôt que selon le type d'exception. Dans ce cas, vous avez besoin de beaucoup de try-catch.

62voto

jmucchiello Points 10521

Je m'ennuyais et j'ai profilé ce qui suit (j'ai laissé de côté le code de synchronisation) :

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 exécutions sur ma boîte WinXP exécuter 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 restaient dans des proportions similaires quel que soit l'ordre dans lequel les tests étaient effectués.

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

0 votes

El except ne lève pas vraiment d'exception. Était-ce le test que vous vouliez faire ?

11 votes

Non, l'intention était de profiler le code qui "peut" gérer les exceptions. Pas de profiler le mécanisme de lancement/attente. Les données démontrent que le simple fait de mettre les blocs try/catch dans votre code n'ajoute pas de surcharge significative.

0 votes

Je pense qu'un tel test est beaucoup trop simple pour tester les exceptions, puisqu'il teste une ligne qui ne peut même pas lancer une exception. Ou bien je ne suis pas au courant de la façon dont la somme et l'affectation peuvent provoquer une exception ?

27voto

Brilliand Points 4167

Les blocs Try-catch ne sont pas un problème de performance - le véritable goulot d'étranglement en matière de performance 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

2 votes

J'ai reproduit ces résultats en utilisant PHP 5.4.22 avec un fichier memory_limit de 128 Mo. +1 Nice benching script

10voto

Aeon Points 3746

En général, utilisez une exception pour vous prémunir contre les échecs inattendus et utilisez le contrôle d'erreur 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 à l'utilisateur en conséquence.

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

Les exceptions sont coûteuses, mais à moins que vous ne gériez tout le déroulement de votre programme en les utilisant, toute différence de performance ne devrait pas être perceptible par l'homme.

6voto

Patrick Desjardins Points 51478

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

2 votes

J'aimerais moi aussi voir le code que vous avez utilisé pour cela, si vous l'avez encore :-)

0 votes

@Patrick Desjardins : pouvez-vous poster le code de votre test ou nous donner un lien vers celui-ci.

3 votes

La boucle qui lance une exception coûte plus cher, car elle lance réellement une exception. Mais si vous n'avez qu'un bloc try-catch, et qu'aucune exception n'est lancée, alors le code n'est pas plus lent comme @jmucchiello l'a testé. Vous n'avez donc pas testé les performances du bloc try-catch, mais celles de la gestion des exceptions, qui est bien sûr beaucoup plus lente. C'est bien sûr utile de le savoir, car après votre test, je sais qu'il est inutile d'utiliser les exceptions pour contrôler le flux de votre application. Les exceptions ne sont utiles que lorsqu'elles sont rares et qu'elles vous aident à gérer les états d'erreur de l'application. Voir aussi la réponse de Steve Jessop.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X