Performance Cost Of 'try' in C#

前端 未结 5 1269
名媛妹妹
名媛妹妹 2020-11-28 12:38

I know that exceptions have a performance penalty, and that it\'s generally more efficient to try and avoid exceptions than to drop a big try/catch around everything -- but

5条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-11-28 13:17

    A common saying is that exceptions are expensive when they are caught - not thrown. This is because most of the exception metadata gathering (such as getting a stack trace etc.) only really happens on the try-catch side (not on the throw side).

    Unwinding the stack is actually pretty quick - the CLR walks up the call stack and only pays heed to the finally blocks it finds; at no point in a pure try-finally block does the runtime attempt to 'complete' an exception (it's metadata etc.).

    From what I remember, any try-catches with filters (such as "catch (FooException) {}") are just as expensive - even if they do not do anything with the exception.

    I would venture to say that a method (call it CatchesAndRethrows) with the following block:

    try
    {
        ThrowsAnException();
    }
    catch
    {
        throw;
    }
    

    Might result in a faster stack walk in a method - such as:

    try
    {
        CatchesAndRethrows();
    }
    catch (Exception ex) // The runtime has already done most of the work.
    {
        // Some fancy logic
    }
    

    Some numbers:

    With: 0.13905ms
    Without: 0.096ms
    Percent difference: 144%
    

    Here is the benchmark I ran (remember, release mode - run without debug):

        static void Main(string[] args)
        {
            Stopwatch withCatch = new Stopwatch();
            Stopwatch withoutCatch = new Stopwatch();
    
            int iterations = 20000;
    
            for (int i = 0; i < iterations; i++)
            {
                if (i % 100 == 0)
                {
                    Console.Write("{0}%", 100 * i / iterations);
                    Console.CursorLeft = 0;
                    Console.CursorTop = 0;
                }
    
                CatchIt(withCatch, withoutCatch);
            }
    
            Console.WriteLine("With: {0}ms", ((float)(withCatch.ElapsedMilliseconds)) / iterations);
            Console.WriteLine("Without: {0}ms", ((float)(withoutCatch.ElapsedMilliseconds)) / iterations);
            Console.WriteLine("Percent difference: {0}%", 100 * withCatch.ElapsedMilliseconds / withoutCatch.ElapsedMilliseconds);
            Console.ReadKey(true);
        }
    
        static void CatchIt(Stopwatch withCatch, Stopwatch withoutCatch)
        {
            withCatch.Start();
    
            try
            {
                FinallyIt(withoutCatch);
            }
            catch
            {
            }
    
            withCatch.Stop();
        }
    
        static void FinallyIt(Stopwatch withoutCatch)
        {
            try
            {
                withoutCatch.Start();
                ThrowIt(withoutCatch);
            }
            finally
            {
                withoutCatch.Stop();
            }
        }
    
        private static void ThrowIt(Stopwatch withoutCatch)
        {
            throw new NotImplementedException();
        }
    

提交回复
热议问题