Насколько дороги исключения в C#?


насколько дороги исключения в C#? Похоже, что они не невероятно дороги, пока стек не глубок; однако я читал противоречивые отчеты.

есть ли окончательный отчет, который не был опровергнут?

7 58

7 ответов:

Джон Скит написал исключения и производительность в .NET в январе 2006

, который был обновлен исключения и снижение производительности (спасибо @Gulzar)

к которому присоединился Рико Мариани истинная стоимость исключений .NET -- Solution


также ссылка: Кшиштоф Cwalina -Обновление Руководящих Принципов Проектирования: Исключение Бросая

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

тем не менее, мое воспоминание о том, как обрабатываются исключения, по существу идет вверх по стеку, находя оператор catch, который соответствует типу вызванного исключения. Таким образом, производительность будет зависеть от того, насколько глубоко вы находитесь от поймайте и сколько у вас есть утверждений catch.

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

программе потребовалось менее 29914 миллисекунд, чтобы обработать миллион исключений, что составляет 33 исключения в миллисекунду. Это достаточно быстро, чтобы сделать исключения жизнеспособной альтернативой кодам возврата для большинства положения.

исключения по крайней мере в 30 000 раз медленнее, чем коды возврата. Как подчеркнул Рико Мариани эти числа также являются минимальными числами. На практике выбрасывание и отлов исключения займет больше времени.

измеряется на ноутбуке с Intel Core2 Duo T8100 @ 2,1 ГГц с .NET 4.0 в сборке выпуска не запускается под отладчиком (что сделало бы его путь медленнее).

Это мой тестовый код:

static void Main(string[] args)
{
    int iterations = 1000000;
    Console.WriteLine("Starting " + iterations.ToString() + " iterations...\n");

    var stopwatch = new Stopwatch();

    // Test exceptions
    stopwatch.Reset();
    stopwatch.Start();
    for (int i = 1; i <= iterations; i++)
    {
        try
        {
            TestExceptions();
        }
        catch (Exception)
        {
            // Do nothing
        }
    }
    stopwatch.Stop();
    Console.WriteLine("Exceptions: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");

    // Test return codes
    stopwatch.Reset();
    stopwatch.Start();
    int retcode;
    for (int i = 1; i <= iterations; i++)
    {
        retcode = TestReturnCodes();
        if (retcode == 1)
        {
            // Do nothing
        }
    }
    stopwatch.Stop();
    Console.WriteLine("Return codes: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");

    Console.WriteLine("\nFinished.");
    Console.ReadKey();
}

static void TestExceptions()
{
    throw new Exception("Failed");
}

static int TestReturnCodes()
{
    return 1;
}

объекты исключения Barebones в C# довольно легкие; обычно это возможность инкапсулировать InnerException Это делает его тяжелым, когда дерево объектов становится слишком глубоким.

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

в моем случае, исключение составили очень дорого. Я переписал это:

public BlockTemplate this[int x,int y, int z]
{
    get
    {
        try
        {
            return Data.BlockTemplate[World[Center.X + x, Center.Y + y, Center.Z + z]];
        }
        catch(IndexOutOfRangeException e)
        {
            return Data.BlockTemplate[BlockType.Air];
        }
    }
}

в:

public BlockTemplate this[int x,int y, int z]
{
    get
    {
        int ix = Center.X + x;
        int iy = Center.Y + y;
        int iz = Center.Z + z;
        if (ix < 0 || ix >= World.GetLength(0)
            || iy < 0 || iy >= World.GetLength(1)
            || iz < 0 || iz >= World.GetLength(2)) 
            return Data.BlockTemplate[BlockType.Air];
        return Data.BlockTemplate[World[ix, iy, iz]];
    }
}

и заметил хорошее увеличение скорости около 30 секунд. Эта функция вызывается не менее 32K раз при запуске. Код не так ясно, что такое намерение, но экономия средств была огромной.

хит производительности с исключениями, кажется, находится в точке генерации объекта исключения (хотя и слишком мал, чтобы вызвать какие-либо проблемы 90% времени). Поэтому рекомендуется профилировать ваш код-если исключения are вызывая снижение производительности, вы пишете новый метод высокой производительности, который не использует исключения. (Пример, который приходит на ум, был бы (TryParse введен для преодоления проблем perf с синтаксическим анализом, который использует исключения)

Что сказал, исключения в большинстве случаев не вызывают значительных хитов производительности в большинстве случаев - так MS Design Guideline должен сообщать о сбоях, бросая исключения

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

     for(; ; ) {
        iValue = Level1(iValue);
        lCounter += 1;
        if(DateTime.Now >= sFinish) break;
     }

vs

     for(; ; ) {
        try {
           iValue = Level3Throw(iValue);
        }
        catch(InvalidOperationException) {
           iValue += 3;
        }
        lCounter += 1;
        if(DateTime.Now >= sFinish) break;
     }

разница составляет 20 раз. Второй фрагмент в 20 раз медленнее.