Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Exception Filter Performance

Do C# exception filters introduced in VS2015 have any effect on performance, memory usage or the stack when an exception is thrown?

Exception filter:

try { … }
catch (Exception e) when (e.Message == "Hello world")
{
    // do stuff
}

Verses traditional catch and re-throw:

try { … }
catch (Exception e)
{
    if (e.Message == "Hello world")
    {
        // do stuff
    }
    else
    {
        throw;
    }
}
like image 333
James Newton-King Avatar asked Oct 28 '25 13:10

James Newton-King


2 Answers

Summary

BenchmarkDotNet=v0.11.5, OS=Windows ..

Intel Core i7, .NET Core 2.2.5

+-----------------+--------+----------------------+-----------------+----------------+-----------------+------+------------+-------+-------+------------+
| Method          | N      | SearchedQueryIsMatch | Mean            | Error          | StdDev          | Rank | Gen 0      | Gen 1 | Gen 2 | Allocated  |
+-----------------+--------+----------------------+-----------------+----------------+-----------------+------+------------+-------+-------+------------+
| ExceptionFilter | 1      | False                | 21.29 us        | 0.3780 us      | 0.6912 us       | 2    | 0.0916     | -     | -     | 440 B      |
| IfStatement     | 1      | False                | 40.35 us        | 0.7339 us      | 0.6505 us       | 3    | 0.0610     | -     | -     | 440 B      |
| ExceptionFilter | 1      | True                 | 19.28 us        | 0.3831 us      | 0.8409 us       | 1    | 0.0305     | -     | -     | 216 B      |
| IfStatement     | 1      | True                 | 19.08 us        | 0.4230 us      | 0.6331 us       | 1    | 0.0305     | -     | -     | 216 B      |
| ExceptionFilter | 1000   | False                | 20,813.47 us    | 413.2388 us    | 537.3272 us     | 5    | 93.7500    | -     | -     | 440000 B   |
| IfStatement     | 1000   | False                | 40,412.30 us    | 645.9158 us    | 604.1901 us     | 6    | 76.9231    | -     | -     | 440000 B   |
| ExceptionFilter | 1000   | True                 | 18,433.85 us    | 257.8815 us    | 228.6052 us     | 4    | 31.2500    | -     | -     | 216000 B   |
| IfStatement     | 1000   | True                 | 18,510.49 us    | 366.2362 us    | 324.6588 us     | 4    | 31.2500    | -     | -     | 216000 B   |
| ExceptionFilter | 100000 | False                | 2,037,740.01 us | 46,797.1438 us | 57,471.0953 us  | 8    | 10000.0000 | -     | -     | 44000000 B |
| IfStatement     | 100000 | False                | 4,057,642.15 us | 80,944.2280 us | 179,366.8182 us | 9    | 10000.0000 | -     | -     | 44000000 B |
| ExceptionFilter | 100000 | True                 | 1,835,382.75 us | 35,810.5411 us | 42,629.9019 us  | 7    | 5000.0000  | -     | -     | 21600000 B |
| IfStatement     | 100000 | True                 | 1,833,703.56 us | 34,189.6215 us | 31,980.9932 us  | 7    | 5000.0000  | -     | -     | 21600000 B |
+-----------------+--------+----------------------+-----------------+----------------+-----------------+------+------------+-------+-------+------------+

Legends

  • N : Value of the 'N' parameter
  • SearchedQueryIsMatch : Value of the 'SearchedQueryIsMatch' parameter
  • Mean : Arithmetic mean of all measurements
  • Error : Half of 99.9% confidence interval
  • StdDev : Standard deviation of all measurements
  • Rank : Relative position of current benchmark mean among all benchmarks (Arabic style)
  • Gen 0 : GC Generation 0 collects per 1000 operations
  • Gen 1 : GC Generation 1 collects per 1000 operations
  • Gen 2 : GC Generation 2 collects per 1000 operations
  • Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  • 1 us : 1 Microsecond (0.000001 sec)

Sample code

    public class Program
    {
        [CoreJob]
        [RPlotExporter, RankColumn, MemoryDiagnoser]
        public class CollectionsContains
        {
            private const string SearchedMessage = "hello world";

            [Params(1, 1_000, 100_000)]
            private int N;

            [Params(true, false)]
            private bool SearchedQueryIsMatch;

            [Benchmark]
            public void ExceptionFilter() => ExecuteTestFor(exception =>
            {
                try
                {
                    throw exception;
                }
                catch (Exception ex) when (ex.Message == SearchedMessage)
                {

                }
            });

            [Benchmark]
            public void IfStatement() => ExecuteTestFor(exception =>
            {

                try
                {
                    throw exception;
                }
                catch (Exception ex)
                {
                    if (ex.Message == SearchedMessage)
                    {
                        return;
                    }

                    throw;
                }

            });

            private void ExecuteTestFor(Action<Exception> testedExceptionHandling)
            {
                for (int i = 0; i < N; i++)
                {
                    try
                    {
                        var exception = new Exception(SearchedQueryIsMatch ? SearchedMessage : Guid.NewGuid().ToString());
                        testedExceptionHandling(exception);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private static void Main() => BenchmarkRunner.Run<CollectionsContains>();
    }
like image 91
Ciapeczka Avatar answered Oct 30 '25 02:10

Ciapeczka


The new C# 6.0 feature of exception filtering basically flips the logic around of catching an exception and then checking conditions.

The difference is:

  • In the first example, you check the condition first, then possibly catch an exception.
  • In the second example, you catch the exception every time, then decide what to do based on the conditions inside.

So I'm not sure what the exact performance implications are, but I think you're better off overall.

If the condition's not met, then you don't have to unwind the stack, or incur the cost of catching and rethrowing (the exception isn't caught/rethrown.. it's just not caught at all), or perform any of the other logic you may have included inside the catch statement.

like image 20
Grant Winney Avatar answered Oct 30 '25 03:10

Grant Winney



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!