The accepted wisdom regarding performance of try/catch|finally in C# has normally been: try has no performance side-effects unless an exception is thrown.
A discussion I was involved in recently caused me to discover some performance implications of try/catch blocks. The discussion revolved around protecting the volatility of certain members and the cross-thread memory ordering rules during run-time and during just-in-time compilation. As it turns out, Microsoft’s x86 .NET 2.0 just-in-time compiler disables optimizations that would affect the CIL order of read/writes in protected blocks (AKA “protected regions”, “guarded blocks”, or “try blocks”). As a result no optimizations are performed in try blocks.As it turns out, there are performance side-effects to try/catch even if no exceptions are thrown. In the following academic example:
int count = 1;SomeMethod();
count++;
SomeOtherMethod();
count++;
Console.WriteLine(count);
The x86 just-in-time compiler will effectively optimize this as follows:
SomeMethod();SomeOtherMethod();
Console.WriteLine(3);
The end-result is the same but the side-effects (that would normally be only visible in a debugger) are different (e.g. count never has the value 2). The just-in-time compiler can do this because it knows no other code can see count when it has the value 2.
Now, wrapping this in a try block, as follows:
int count = 1;try
{
SomeMethod();
count++;
SomeOtherMethod();
count++;
}
finally
{
Console.WriteLine(count);
}
…is not optimized. count will be created, it will have the value 1, it will have the value 2 after the call to SomeMethod, it will have the value 3 after SomeOther method, and will have the value 3 when Console.WriteLine is called. There are two increments that therefore must be performed that are a waste of cycles.
So, be careful how you write code in try blocks.
As always excellent tidbit. I’ve been reading your blog and reading your e-mails on Developmentor’s .NET lists and I must say I learn from you every week. Thanks for keeping it interesting and keep up the good work 🙂
Is the effect, loss of optimization, the same if you move the scope of the try/catch to an outside method calling a function which has the code…or at what point, if any does, optimization come back into play?
@OmegaMan: I’m finding it’s not as cut-and-dry as “no optimizations are performed”. To a certain extent it depends on what is and isn’t in the try block. It appears a more accurate statement would be “no optimizations are performed that would affect reordering of reads/writes of variables used inside AND outside the try block”; but, I’m following up on that.
Does this mean that locked code is not optomized? e.g:
Lock(this)
{
SomeMethod();
count++;
SomeOtherMethod();
count++;
Console.WriteLine(count);
}
Yes, lock uses try/catch, it will have the same performance implications.
Theoretically the try part of this try statement doesn’t have to disable optimization, but the finally part has to disable (to some extent) optimization of the try part. The reason is that the finally part fetches the value of count.
As an analogy, suppose you compare two programs, one with trivial uses of count the way your first program did, and the other one with a for loop and a call to some function with count as an argument in that function call. Would you say that the for loop causes the decrease in optimization, or would you say that passing count as an argument causes the decrease in optimization?
@Norman. Yes, due to various guarantees it has to disable *some* optimizations in the try block.
For example:
try
{
i = 1;
i = 2;
methodThatMightThrow();
i = 3;
Trace.WriteLine(i);
}
catch(Exception)
{
}
Trace.WriteLine(i);
The compiler/runtime guarantees that the state of i will be 2 when the exception is thrown (and that i will be 1 if an exception occurs before setting it to 2–remember there are asynchronous exceptions). Normal optimizations would think that i only ever really needs to be 3 and would optimize as such.
See also http://msmvps.com/blogs/peterritchie/archive/2007/07/12/performance-implications-of-try-catch-finally-part-two.aspx
Peter,
Thanks for helping me understand one of the often ignored costs of throwing exceptions. I think too often we ignore the fact that what we write and the bytecode are not exactly the same thing.
In a business layer where we are searching for records is there much of a performace hit to throw a custom exception like ProductNotonFileException instead of null for example.
@Eugen. I prefer returning null for “search-like” method. I would to performance tests specifically in your circumstance; but my tests show that throwing/catching an exception over returning null is only about 5% slower. If you don’t plan on throwing many exceptions, the difference is essentially negligible.
It’s not just that the jitter doesn’t optimize away the count variable entirely, it would be illegal to. It’s possible for the output of the second program to be “1”, “2” or “3”, depending on which (if any) function calls throw an exception. This makes it a fundamentally different program from the first (which will output “3” when it outputs anything). The moral of the story isn’t that you should be wary of losing optimization opportunities, it’s that you should understand what try/finally does and use it properly.
It would be interesting if there were other *legal* optimizations that aren’t made, but your example doesn’t show this.
Do we want this code optimised, though? The coding suggests that if SomeMethod fails, we want count == 2, and if SomeOtherMethod fails, we want count == 3.
Um, you really should consider removing this post. It would be bad enough if a newbie spouted off such nonsense, but as a MVP it is just disgraceful.
@d * @bmm60 Exactly, we don’t want it to optimise this code. The point of the post is to dispel the conventional wisdom that using try/catch does not impact performance; this shows that it does.
@Jonathan Allen: In what way do you think this is nonsense?
@Peter The examples are two different programs. They have different outputs depending on when an exception is thrown. They are not the same and thus the optimizations are not the same. If you wanted to point out that similar programs with different output might have different runtime performance, then keep the article. But, it does not prove anything about the lack of optimizations in a try/catch/finally block.
If I can summarize, I think the criticisms between your non-try code and your try-blocked code is that they’re not really functional equivalents. I’m a linguist, not a computer-scientists so I apologize if the jargon is off — but in the non-try code, the value of the variable inside WriteLine is always 3 regardless of what happens with the other functions being called.
The JIT-compiled code is — absolutely and of course — different between the two because the try-blocked code has not one but THREE potential outcomes:
int count = 1;
try
{
SomeMethod(); ## Raise exception here count=1
count++;
SomeOtherMethod(); ## Raise exception here count=3
count++;
}
The functionally-equivalent code, then, is not something that runs two functions and makes count == 3, but rather, the possibility of a raised exception means that there are two implicit conditionals within the try block where there are not within the non-try-block’d code.
So in order to instantiate the conditionals implied within the try block due to the raised exception, you’d have to do something like:
int count = 1;
if someMethod(){
count++
if someOtherMethod(){
count++}
}
Console.WriteLine(count);
I guess I’d be curious to know: is there a performance difference between THAT and the try-finally code?
@foobar,that’s kind of the point. How do you show how using try/catch affects performance if you don’t have “two programs”?
@Chuck. That’s the point; the mere introduction of a try block now means there are three explicit potential outcomes. Previously they where only implicit and could be optimized away because they were implicit. The only difference between the two snippets of code is the try/catch. Thus, “using try/catch does not affect performance” is not true (the point of the post).
Thanks for appropriately reading thru the typos in my post 🙂
On further review, I see all that should’ve been clear, but I thank you also for accommodating me in my paraphrase of your point.
> The point of the post is to dispel the conventional wisdom that using try/catch does not impact performance; this shows that it does.
I disagree. This article shows that changing the requirements of a program can result in an implementation that has different performance characteristics. Moving the WriteLine to the finally block means you’re comparing apples to oranges; the programs aren’t interchangeable implementations of the same spec, one using try/finally and the other not, they implement different specifications.
I mean, it’s true that the second program can’t be optimized in the same way that the first one can. I think it’s invalid to draw general conclusions about that from this example. What if you rewrite it in the style Norman suggests?
@bmm60: The point of the article *is* to show that using try/catch does impact performance. What you take out of the post, is up to you. How, otherwise, would you show that try/catch does affect performance if you don’t show the analysis of two different snippets of code–that differ only by the addition of a try/catch?
It’s true that the second can’t be optimized the same as the first; but that’s because of the added try/catch (or try/finally in C#).
Changing the code to what Norman suggests (i.e. comparing linear code to looping code with a try/catch) most certainly would be comparing apples to oranges. It would then be the very post you’re saying it already is. It wouldn’t tell you anything–you couldn’t see the difference in optimizations of the same code with and without a container try/catch.
The code *could* use a catch block and push the WriteLine outside the try/catch; but, it would show the same thing without adding any clarity, IMO.
May be a silly question, but since SomeMethod and SomeOtherMethod are in the try catch block, are their code blocks also not optimized?
Thanks.
Nope, not a silly question. No, a method call within a try/catch has no effect on the body of the method. The method may be jitted before the code with the try/catch is jitted; so, a try/catch block can’t affect the body of another method.
Why is the scope of count outside of the try block? Why is the WriteLine outside of the try block?
What I’m pointing out is that they are two completely different programs and demonstrates nothing about optimization.
It’s example code, none of the code has a reason other than to show the contents of a try block cannot be optimized by the JIT compiler like the same code without a try block is optimized.
PeterRitchie perhaps you’re misunderstanding why many people are complaining about your example.
The code cannot be optimized, not because of the try block, but rather that the code is functionally different from the preceding code. Put the entire block of code within the try block and you’ll see it can be optimized in the exact same manner.
Create a new example where the exact same code can be put into a try block and not be optimized and you’ll have a good article.
Perhaps this flawed statement would help you understand: “Adding an If statement/block can prevent the code from being optimized in the same manner, thus no optimization can be done within an if block!” That’s what your article is foolishly saying about the try/catch block.
I should clarify “no optimizations are performed in try blocks”… Maybe another block post. An if statement is not a protected block and could not affect optimization like a try block can. Part of my point is that try blocks *are* protected blocks and those protected blocks limit how the code can be optimized–but only with regard to what is observable outside the try block.