Почему трассировка блоков стоит дорого?

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

Мой вопрос конкретно о платформе .NET: почему трассировка блоков стоит дорого?

Резюме ответов:

В этой проблеме явно есть два лагеря: те, которые говорят, что try-блоки дороги, и те, которые говорят "может быть, крошечный бит".

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

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

Была одна статья, которую я нашел чрезвычайно интересной: она говорила о "других" эффектах производительности блоков try (не обязательно в памяти или потреблении процессора). Питер Ритчи упоминает, что он обнаружил, что код внутри блоков try не оптимизирован, как это было бы в случае с компилятором. Вы можете прочитать о его результатах здесь.

Наконец, есть запись в блоге о проблеме от человека, который реализовал исключения в CLR. Пойдите взгляните на статью Криса Брумме здесь.

Ответ 1

Не самый дорогостоящий блок, и он даже не поймал исключение, как таковое, дорогое, это время выполнения, которое разматывает стек вызовов, пока не найдет фрейм стека, который может обрабатывать исключение. Выброс исключения - довольно легкий вес, но если среда выполнения должна пройти шесть кадров стека (т.е. шесть вызовов методов в глубину), чтобы найти соответствующий обработчик исключений, возможно, выполняя, наконец, блоки, по мере того, как это происходит, вы можете заметить заметное количество времени.

Ответ 2

Вам не следует избегать блоков try/catch, поскольку это обычно означает, что вы неправильно обрабатываете исключения, которые могут возникнуть. Обработка структурированных исключений (SEH) стоит только дорого, когда на самом деле происходит исключение, так как среда выполнения должна идти в стек вызовов, ищущий обработчик catch, выполнять этот обработчик (и может быть несколько), затем выполнять блоки finally, а затем возвращать верните код в нужное место.

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

Одно из самых больших заблуждений об исключениях заключается в том, что они предназначены для "исключительные условия". Реальность что они предназначены для общения условия ошибки. Из рамок проектной точки зрения, таких как "исключительное условие". Независимо от того, является ли условие исключительным или не зависит от контекста использования, --- но библиотеки многократного использования редко знают, как они будут использоваться. Например, OutOfMemoryException может быть исключительный для простого ввода данных выражение; его не столь исключительный для приложений, выполняющих собственные управления памятью (например, SQL-сервер). Другими словами, один человек исключительный состояние - другое, хроническое состояние. [ http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx]

Ответ 3

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

Я выполнил следующий тест, бросив и поймав миллион исключений. Это заняло около 20 секунд на моем Intel Core 2 Duo, 2,8 и nbsp; GHz. Это около 50 тыс. Исключений в секунду. Если вы выбрасываете хотя бы небольшую часть, у вас есть проблемы с архитектурой.

Здесь мой код:

using System;
using System.Diagnostics;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                try
                {
                    throw new Exception();
                }
                catch {}
            }
            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.Read();
        }
    }
}

Ответ 4

Блок try вообще не дорог. Небольшая или никакая стоимость не понесена, если не будет выбрано исключение. И если выбрано исключение, это обстоятельство исключительное, и вы больше не заботитесь о производительности. Имеет ли значение, если ваша программа занимает 0,001 секунды или 1,0 секунды, чтобы упасть? Нет. Важно то, насколько хороша информация, сообщенная вам, чтобы вы могли ее исправить и остановить ее снова.

Ответ 5

Компилятор испускает больше IL, когда вы завершаете код внутри блока try/catch; Посмотрите, для следующей программы:

using System;
public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("abc");
    }
}

Компилятор испустит этот IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "abc"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Program::Main

Пока для слегка измененной версии:

using System;
public class Program
{
    static void Main(string[] args)
    {
        try { Console.WriteLine("abc"); }
        catch { }
    }
}

испускает больше:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  1
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "abc"
    IL_0007:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000c:  nop
    IL_000d:  nop
    IL_000e:  leave.s    IL_0015
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0010:  pop
    IL_0011:  nop
    IL_0012:  nop
    IL_0013:  leave.s    IL_0015
  }  // end handler
  IL_0015:  nop
  IL_0016:  ret
} // end of method Program::Main

Все эти NOP и другие расходы.

Ответ 6

IMO все это обсуждение похоже на то, что "wow lops стоят дорого, потому что мне нужно увеличивать счетчик... я больше их не буду использовать", или "ничего себе, создание объекта требует времени, я не собираюсь создать тонну объектов больше."

Нижняя строка - это ваш код добавления, предположительно по какой-то причине. Если строки кода не привели к некоторым накладным расходам, даже если их 1 процессорный цикл, то почему он существует? Ничто не является бесплатным.

Мудрая вещь, которую нужно сделать, как и любая строка кода, которую вы добавляете в ваше приложение, заключается только в том, чтобы положить ее туда, если вам нужно что-то сделать. Если ловить исключение - это то, что вам нужно сделать, тогда сделайте это... просто, если вам нужна строка для хранения чего-то, создайте новую строку. Точно так же, если вы объявляете переменную, которая никогда не используется, вы теряете память и циклы CPU, чтобы создать ее, и ее нужно удалить. то же самое с try/catch.

Другими словами, если там есть код, чтобы сделать что-то, тогда предположите, что выполнение чего-то будет потреблять процессор и/или память каким-то образом.

Ответ 7

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

Ответ 8

Я сомневаюсь, что они особенно дороги. Много раз они необходимы/необходимы.

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

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

Ответ 9

Это не то, о чем я когда-либо волновался. Я предпочел бы заботу о ясности и безопасности попытки... наконец, блокировать себя в отношении того, как это "дорого".

Я лично не использую 286, и никто не использует .NET или Java. Двигаться дальше. Беспокоитесь о написании хорошего кода, который повлияет на ваших пользователей и других разработчиков вместо базовой структуры, которая отлично работает для 99.999999% людей, использующих ее.

Это, вероятно, не очень полезно, и я не собираюсь скучать, а просто подчеркиваю перспективу.

Ответ 10

Слегка O/T, но...

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

Как можно сказать "writeable()" перед "write()", вот так.

Это приличная идея, и если она используется, она делает проверенные исключения в Java выглядящими добрыми глупостями - я имею в виду, проверяя условие и сразу после этого, будучи вынужденным еще написать try/catch для того же условия?

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

Ответ 11

Каждая попытка должна записывать много информации, например. указатели стека, значения регистров CPU и т.д., чтобы он мог разматывать стек и возвращать состояние, которое было при передаче блока try в случае возникновения исключения. Мало того, что каждая попытка должна записывать много информации, когда генерируется исключение, необходимо восстановить множество значений. Таким образом, попытка очень дорога, и бросок/улов тоже очень дорог.

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