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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Я проверил следующий тест, бросив и поймав миллион исключений. Это заняло около 20 секунд на моем процессоре Intel Core 2 Duo , 2,8 ГГц. Это около 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(); } } } 

Компилятор испускает больше 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 и другие стоят.

ИМО вся эта дискуссия похожа на то, что «wow lops стоят дорого, потому что мне нужно увеличивать счетчик … я больше их не буду использовать», или «ничего не требуется для создания объекта, я не собираюсь создавать тонна объектов больше. ”

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

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

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

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

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

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

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

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

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

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

Немного O / T, но …

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

Подобно тому, как говорить «writeable ()» перед «write ()», вот так.

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

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

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

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

  • Как создать элементарный stream AAC ADTS с Android MediaCodec
  • Пусть объект JSON принимает байты или позволяет выводить строки вывода
  • Какова правильная кодировка HTTP-запросов?
  • Как изменить кодировку по умолчанию в NetBeans 8.0
  • Почему значения cookie с пробелом поступают на стороне клиента с кавычками?
  • Excel в CSV с кодировкой UTF8
  • Как исправить кодировку символа файла?
  • Файлы заголовков C ++, разделение кода
  • Base64 кодирует строку в VBScript
  • Какую кодировку использует Microsoft Excel при сохранении файлов?
  • Кодировать NSString для XML / HTML
  • Давайте будем гением компьютера.