2009-07-10 12 views
0

J'ai réfléchi à des instructions try/catch imbriquées et j'ai commencé à réfléchir aux conditions, le cas échéant, dans lesquelles le JIT peut effectuer une optimisation ou une simplification de l'IL compilé. Pour illustrer, considérons les représentations fonctionnellement équivalentes suivantes d'un gestionnaire d'exceptions.Le .NET JIT optimise-t-il les instructions try/catch imbriquées?

// 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 de 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éduit à la précédente. Cependant, dans le cas de foo() en lançant ExceptionC, cette solution est-elle moins performante que l'exemple linéaire? Je soupçonne qu'il y a un coût supplémentaire pour démolir les cadres de pile à partir des invocations de délégué, même si les données supplémentaires contenues dans les cadres sont minimes.

+1

Avez-vous regardé l'IL pour les deux premiers cas? – womp

Répondre

8

Il est à noter que dans le premier cas, ils sont seulement équivalent fonctionnellement lorsque vous ne 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"); 
} 

Or, 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é ? Mieux vaut être prudent.

Cela nous laisse l'option du JIT d'effectuer des optimisations pour les blocs de capture vides - une pratique qui est fortement déconseillée en premier lieu. Je ne veux pas que le JIT passe du temps à essayer de détecter un mauvais code et à le faire tourner un peu plus vite - s'il y a une différence de performance en premier lieu.

+0

Merci pour l'analyse Jon. Dans mon esprit, les manutentionnaires vides effectuaient des opérations sans lancer. Cependant, votre analyse s'applique toujours car des exceptions comme OutOfMemoryException et ThreadAbortExcetpion peuvent se produire pour des raisons externes à foo(). –

4

Ma compréhension des régions try/catch/finally en ce qui concerne les performances est que ces régions sont transparentes à l'exécution régulière du code. En d'autres termes, si votre code ne génère aucune exception, les régions try/catch/finally ont un impact sur les performances d'exécution du code. Cependant, lorsqu'une exception est déclenchée, le runtime commence à remonter la pile à partir du site où il est déclenché, en vérifiant les tables de métadonnées pour voir si le site en question est contenu dans l'un des blocs try critiques. Si l'on en trouve un (et qu'il a un bloc de capture admissible ou un bloc finally), le gestionnaire correspondant est identifié et l'exécution se ramifie à ce point.

Le processus d'augmentation et de gestion des exceptions est coûteux du point de vue des performances. Les programmeurs ne devraient pas utiliser les exceptions comme un moyen de signaler ou de contrôler le déroulement du programme dans des circonstances autres que des circonstances exceptionnelles (jeu de mots.)

+0

+1 pour expliquer le coût d'exécution des blocs try-catch –