Производительность Стоимость «попробовать» в C # - PullRequest
20 голосов
/ 15 мая 2009

Я знаю, что исключения приводят к снижению производительности, и что в целом более эффективно пытаться избегать исключений, чем бросать большие попытки / ловить вокруг всего - но как насчет самого блока try? Сколько стоит простое объявление попытки / улова, даже если оно никогда не вызывает исключение?

Ответы [ 5 ]

26 голосов
/ 15 мая 2009

Стоимость исполнения очень мала. Основная стоимость обработки исключений заключается в получении трассировки стека и других метаданных, и эта стоимость не оплачивается до тех пор, пока вам фактически не придется выдавать исключение.

Но это будет зависеть от языка и реализации. Почему бы не написать простой цикл в C # и не рассчитать его время самостоятельно?

7 голосов
/ 15 мая 2009

На самом деле, пару месяцев назад я создавал веб-приложение ASP.NET и случайно обернул блок try / catch очень длинным циклом. Хотя цикл не генерировал все исключения, он занимал слишком много времени, чтобы закончить. Когда я вернулся и увидел try / catch, обернутый циклом, я сделал это наоборот, я обернул цикл в блок try / catch. Производительность улучшена много. Вы можете попробовать это самостоятельно: сделать что-то вроде

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());

А затем вытащите блок try / catch. Вы увидите большую разницу!

5 голосов
/ 15 мая 2009

Поговорка гласит, что исключения обходятся дорого, когда их ловят, а не бросают. Это связано с тем, что большинство сбора метаданных исключений (например, получение трассировки стека и т. Д.) Действительно происходит только на стороне try-catch (не на стороне throw).

Развертывание стека на самом деле довольно быстрое - CLR поднимается вверх по стеку вызовов и обращает внимание только на найденные им блоки finally; ни в одной точке чистого блока try-finally среда выполнения не пытается «завершить» исключение (это метаданные и т. д.).

Из того, что я помню, любые попытки получения с фильтрами (такие как "catch (FooException) {}") столь же дороги - даже если они ничего не делают за исключением.

Рискну сказать, что метод (назовите его CatchesAndRethrows) со следующим блоком:

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

Может привести к более быстрому обходу стека в методе, например:

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

Некоторые цифры:

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

Вот тест, который я запустил (помните, режим выпуска - запустите без отладки):

    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();
    }
4 голосов
/ 04 ноября 2013

Чтобы увидеть, что это действительно стоит, вы можете запустить код ниже. Он принимает простой двумерный массив и генерирует случайные координаты, которые находятся вне диапазона. Если ваше исключение происходит только один раз, конечно, вы не заметите это. Мой пример сделан для того, чтобы подчеркнуть, что это будет означать, если вы будете делать это несколько тысяч раз, и то, что отлов исключений и реализация простого теста спасет вас.

        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();

Пример вывода этого кода:

---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---
1 голос
/ 15 мая 2009

Возможно, вы захотите ознакомиться со структурной обработкой исключений. Это реализация исключений Window, используемая в .NET.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...