Optimization
The compiler will not optimize this out, even in a Release build.
Take the following test application:
public class Program {
public static void Main(string[] args) {
try {
CallsThrow();
}
catch(Exception ex) {
Console.WriteLine("Main() caught exception: " + ex.Message);
}
Console.Read();
}
private static void CallsThrow() {
try {
Throw();
}
catch {
throw;
}
}
private static void Throw() {
throw new Exception("Here's my exception.");
}
}
Using ILSpy we can look at the output binary at the IL level. We see that the try
/catch
in CallsThrow
is still there in our Release binary:
.method private hidebysig static
void CallsThrow () cil managed
{
// Method begins at RVA 0x2094
// Code size 11 (0xb)
.maxstack 1
.try
{
IL_0000: call void JunkCSharpConsole.Program::Throw()
IL_0005: leave.s IL_000a
} // end .try
catch [mscorlib]System.Object
{
IL_0007: pop
IL_0008: rethrow
} // end handler
IL_000a: ret
} // end of method Program::CallsThrow
Benchmark
Code:
public class Program
{
public static void Main(string[] args) {
const int N = 100000000;
#if DEBUG
const string mode = "Debug";
#else
const string mode = "Release";
#endif
Console.WriteLine("Testing {0} iterations in {1} mode:", N, mode);
// Attempt to JIT / cache
CallsThrowWithTryCatch(false);
CallsThrowWithoutTryCatch(false);
// Test with try/catch+throw
var s1 = Stopwatch.StartNew();
for (int i = 0; i < N; i++ )
CallsThrowWithTryCatch(false);
s1.Stop();
Console.WriteLine(" With try/catch: {0} ms", s1.ElapsedMilliseconds);
// Test without try/catch+throw
var s2 = Stopwatch.StartNew();
for (int i = 0; i < N; i++)
CallsThrowWithoutTryCatch(false);
s2.Stop();
Console.WriteLine(" Without try/catch: {0} ms", s2.ElapsedMilliseconds);
var pct = (s1.ElapsedMilliseconds - s2.ElapsedMilliseconds) / (double)s1.ElapsedMilliseconds * 100.0;
Console.WriteLine("No try/catch faster by {0:.02}%", pct);
// Just show that it works
try {
CallsThrowWithTryCatch(true);
}
catch (Exception ex) {
Console.WriteLine("Main() caught exception: " + ex.Message);
}
// Wait to exit
Console.WriteLine("Press ENTER to exit.");
Console.Read();
}
private static void CallsThrowWithTryCatch(bool doThrow) {
try {
Throw(doThrow);
}
catch {
throw;
}
}
private static void CallsThrowWithoutTryCatch(bool doThrow) {
Throw(doThrow);
}
private static void Throw(bool doThrow) {
if (doThrow)
throw new Exception("Here's my exception.");
}
}
Results:
Testing 100000000 iterations in Debug mode:
With try/catch: 1492 ms
Without try/catch: 1474 ms
No try/catch faster by 1.22%
Main() caught exception: Here's my exception.
Press ENTER to exit.
Testing 100000000 iterations in Release mode:
With try/catch: 598 ms
Without try/catch: 458 ms
No try/catch faster by 23.42%
Main() caught exception: Here's my exception.
Press ENTER to exit.
We can see that yes, there is a performance penalty associated with even the empty try
/catch
. In Debug builds it is not so significant, but a Release build showed a substantial 23.42% improvement by removing the try
/catch
.