Question

J'ai pensé à des déclarations try / catch imbriquées et a commencé à penser dans quelles conditions, le cas échéant, le JIT peut effectuer une optimisation ou la simplification de l'IL compilé.

Pour illustrer ceci, les représentations suivantes fonctionnellement équivalents d'un gestionnaire d'exception.

// Nested try/catch
try
{
  try
  {
    try
    {
      foo();
    }
    catch(ExceptionTypeA) { }
  }
  catch(ExceptionTypeB) { }
}
catch(ExceptionTypeC) { }

// Linear try/catch
try
{
  foo();
}
catch(ExceptionTypeA) { }
catch(ExceptionTypeB) { }
catch(ExceptionTypeC) { }

Si l'on suppose qu'il n'y a pas de références variables supplémentaires ou des appels de fonctions dans les cadres de pile de l'instruction try imbriqué, peut le JIT conclure que les cadres de pile peuvent être réduits à l'exemple linéaire?

Maintenant que diriez-vous l'exemple suivant?

void Try<TException>(Action action)
{
  try
  {
    action();
  }
  catch (TException) { }
}

void Main()
{
  Try<ExceptionC>(Try<ExceptionB>(Try<ExceptionA>(foo)));
}

Je ne pense pas qu'il y ait aucune façon pour le JIT inline les invocations des délégués, donc cet exemple ne peut pas être réduite à la précédente. Toutefois, dans le cas de foo() lancer ExceptionC, cette solution n'effectuer plus faible par rapport à l'exemple linéaire? Je soupçonne qu'il ya un coût supplémentaire pour abattre les cadres de la pile des invocations de délégués, même si les données supplémentaires contenues dans les trames est minime.

Était-ce utile?

La solution

Il convient de noter que, dans le premier cas, ils sont que fonctionnellement équivalent quand vous faites rien dans le bloc catch. Dans le cas contraire, considérez ceci:

try
{
    foo();
}
catch (IOException)
{
    throw new ArgumentException(); // Bubbles up to caller
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

vs

try
{
    try
    {
        foo();
    }
    catch (IOException)
    {
        throw new ArgumentException(); // Caught by other handler
    }
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

Dans ce cas, la différence est évidente, mais si le bloc catch appelle une méthode arbitraire, comment le JIT voulait savoir ce qui pourrait être jeté? Il vaut mieux être prudent.

Cela nous laisse avec l'option du JIT pour effectuer Optimisations blocs catch vides - une pratique qui est fortement déconseillée en premier lieu. Je ne veux pas que le JIT de passer du temps à essayer de détecter le mauvais code et le faire tourner très légèrement plus vite -. En effet, s'il y a une différence de performance en premier lieu

Autres conseils

Ma compréhension de try / catch / finally régions par rapport à la performance est que ces régions sont transparentes pour l'exécution régulière de code. Autrement dit, si votre code ne jette pas des exceptions à attraper, puis les try / catch / finally régions ont ZERO impact sur les performances d'exécution de code.

Toutefois, lorsqu'une exception est levée, le moteur d'exécution commence à marcher la pile à partir du site auquel il est élevé, la vérification des tables de métadonnées pour voir si le site en question est contenu dans l'un des blocs try critiques. Si l'on se trouve (et il a un bloc de capture admissible ou un bloc finally), le gestionnaire concerné est identifié et branches d'exécution à ce point.

Le processus de collecte et de gestion des exceptions est coûteuse du point de vue de la performance. Les programmeurs ne devraient pas utiliser des exceptions comme un moyen de signalisation ou de contrôle des flux de programme autre chose que des circonstances exceptionnelles (jeu de mots).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top