c#异常过滤器性能

本文关键字:性能 过滤器 异常 | 更新日期: 2023-09-27 18:02:17

在VS2015中引入的c#异常过滤器在抛出异常时对性能,内存使用或堆栈有任何影响吗?

异常过滤器:

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

对比传统的接球和再抛:

try { … }
catch (Exception e)
{
    if (e.Message == "Hello world")
    {
        // do stuff
    }
    else
    {
        throw;
    }
}

c#异常过滤器性能

总结

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 |
+-----------------+--------+----------------------+-----------------+----------------+-----------------+------+------------+-------+-------+------------+
<标题>传说h1> li>N: N参数的值
  • SearchedQueryIsMatch: 'SearchedQueryIsMatch'参数的值
  • 平均值:所有测量值的算术平均值
  • 错误:99.9%置信区间的一半
  • StdDev:所有测量值的标准差
  • Rank:当前基准均值在所有基准中的相对位置(阿拉伯字体)
  • 第0代:GC第0代每1000次操作收集
  • 第1代:GC第1代每1000次操作收集
  • 第2代:GC第2代每1000次操作收集
  • 已分配:每个操作分配的内存(仅管理,包括1KB = 1024B)
  • 1 us: 1微秒(0.000001秒)
  • <标题> 示例代码
        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>();
        }
    

    c# 6.0的异常过滤新特性基本上改变了捕获异常然后检查条件的逻辑。

    区别是:

    • 在第一个示例中,首先检查条件,然后可能捕获异常。
    • 在第二个示例中,每次捕获异常,然后根据内部条件决定做什么。

    所以我不确定确切的性能影响是什么,但我认为你总体上更好。

    如果条件不满足,则不必展开堆栈,或者产生捕获和重新抛出的成本(异常不会被捕获/重新抛出)。它只是根本没有被捕获),或者执行您可能包含在catch语句中的任何其他逻辑。