Подтвердить что ты не робот

Производительность "Попробуйте" на С#

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

4b9b3361

Ответ 1

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

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

Ответ 2

Собственно, пару месяцев назад я создавал веб-приложение 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. Вы увидите большую разницу!

Ответ 3

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

Разматывание стека на самом деле довольно быстро - 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

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

        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---

Ответ 5

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

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