I have made this benchmark to compare a code ran with one catch vs multiple.
The result is – multiple catch takes 5% more time (see the code below). I think it makes sense but I just wanted to validate the test with the community. And perhaps get some insights.
Before I get to it, let me explain why. I have some time-sensitive high throughput bits of code in a .NET8 app and I put multiple catch statements in those places because my premise was – if an exception happen – it is faster to process if you have a more specific catch.
After this test the idea is to try to guess – how many exceptions there will be vs how many hits on multiple catch. Ie – cost of single catch exception processing compared to cost of entering multiple catch. What is better on average in each case.
Is the above a correct way to look at it?
Here is the code:
internal class Program
{
static void Main(string[] args)
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Console.WriteLine("Hello, World!");
Console.WriteLine("===================================================");
var n = 10_000_000;
var K = 500;
var t1 = TimeSpan.Zero;
var t2 = TimeSpan.Zero;
for (int k = 0; k < K; k++)
{
var nogc = GC.TryStartNoGCRegion(250 * 1024 * 1024, true);
//Console.WriteLine($"NoGC: {nogc}, Latency: {GCSettings.LatencyMode}");
var sw = Stopwatch.StartNew();
var a = TryCatch(n);
sw.Stop();
t1 += sw.Elapsed;
var latency = GCSettings.LatencyMode;
if (latency == GCLatencyMode.NoGCRegion) GC.EndNoGCRegion();
//Console.WriteLine("Result: {0} in {1}, latency: {2}", a, sw.Elapsed, latency);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
//Console.WriteLine("---------------------------------------------------");
nogc = GC.TryStartNoGCRegion(250 * 1024 * 1024);
sw.Restart();
var b = TryCatch2(n);
sw.Stop();
t2 += sw.Elapsed;
latency = GCSettings.LatencyMode;
if (GCSettings.LatencyMode == GCLatencyMode.NoGCRegion) GC.EndNoGCRegion();
//Console.WriteLine("Result: {0} in {1}, latency: {2}", b, sw.Elapsed, latency);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.Write(".");
}
Console.WriteLine();
Console.WriteLine("===================================================");
Console.WriteLine($"K: {K}t T1: {t1}, T2: {t2}");
Console.WriteLine($"Avg t T1: {t1/K}, T2: {t2/K}");
Console.ReadKey(true);
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
static A DoWork(int arg)
{
var a = new A();
a.N = Math.Log(arg);
return a;
}
static double TryCatch(int n)
{
var res = 0.0;
for (int i = 0; i < n; i++)
{
try
{
res += DoWork(i).N;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
return res;
}
static double TryCatch2(int n)
{
var res = 0.0;
for (int i = 0; i < n; i++)
{
try
{
res += DoWork(i).N;
}
catch (ObjectDisposedException ex)
{
Console.WriteLine(ex);
}
catch (OverflowException ex)
{
Console.WriteLine(ex);
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
return res;
}
}
public class A : IDisposable
{
public double N { get; set; }
public void Dispose()
{
}
}
result:
K: 500 T1: 00:01:00.2907391, T2: 00:01:03.4896285
Avg T1: 00:00:00.1205815, T2: 00:00:00.1269793