Performance Cost Of 'try' in C#

前端 未结 5 1237
名媛妹妹
名媛妹妹 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条回答
  • The performance cost of try is very small. The major cost of exception handling is getting the stack trace and other metadata, and that's a cost that's not paid until you actually have to throw an exception.

    But this will vary by language and implementation. Why not write a simple loop in C# and time it yourself?

    0 讨论(0)
  • 2020-11-28 13:10

    To see what it really costs, you can run the code below. It takes a simple two dimensional array and generates random coordinates which is out of range. If your exception only occurs one time, of course you will not notice it. My example is done to emphasize what it will mean when doing this several thousand times, and what catching an exception vs implementing a simple test will save you.

            const int size = 1000;
            const int maxSteps = 100000;
    
            var randomSeed = (int)(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0).ToLocalTime()).TotalMilliseconds;
            var random = new Random(randomSeed);
            var numOutOfRange = 0;
            var grid = new int[size,size];
            var stopwatch = new Stopwatch();
            Console.WriteLine("---Start test with exception---");
            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < maxSteps; i++)
            {
                int coord = random.Next(0, size * 2);
                try
                {
                    grid[coord, coord] = 1;
                }
                catch (IndexOutOfRangeException)
                {
                    numOutOfRange++;
                }
            }
            stopwatch.Stop();
            Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange);
            Console.WriteLine("---End test with exception---");
    
            random = new Random(randomSeed);
    
            stopwatch.Reset();
            Console.WriteLine("---Start test without exception---");
            numOutOfRange = 0;
            stopwatch.Start();
            for (int i = 0; i < maxSteps; i++)
            {
                int coord = random.Next(0, size * 2);
                if (coord >= grid.GetLength(0) || coord >= grid.GetLength(1))
                {
                    numOutOfRange++;
                    continue;
                }
                grid[coord, coord] = 1;
            }
            stopwatch.Stop();
            Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange);
            Console.WriteLine("---End test without exception---");
            Console.ReadLine();
    

    Example output of this code:

    ---Start test with exception---
    Time used: 3228ms, Number out of range: 49795
    ---End test with exception---
    ---Start test without exception---
    Time used: 3ms, Number out of range: 49795
    ---End test without exception---
    
    0 讨论(0)
  • 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();
        }
    
    0 讨论(0)
  • 2020-11-28 13:19

    You might want to read up on Structured Exception Handling. It's Window's implementation of exceptions and used in .NET.

    http://www.microsoft.com/msj/0197/Exception/Exception.aspx

    0 讨论(0)
  • 2020-11-28 13:27

    Actually, a couple months ago I was creating an ASP.NET web app, and I accidentally wrapped a try / catch block with a very long loop. Even though the loop wasn't generating every exceptions, it was taking too much time to finish. When I went back and saw the try / catch wrapped by the loop, I did it the other way around, I wrapped the loop IN the try / catch block. Performance improved a LOT. You can try this on your own: do something like

    int total;
    
    DateTime startTime = DateTime.Now;
    
    for(int i = 0; i < 20000; i++)
    {
    try
    {
    total += i;
    }
    catch
    {
    // nothing to catch;
    }
    }
    
    Console.Write((DateTime.Now - startTime).ToString());
    

    And then take out the try / catch block. You'll see a big difference!

    0 讨论(0)
提交回复
热议问题