Почему стоит попробовать блоки? - PullRequest
51 голосов
/ 03 октября 2008

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

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

Сводка ответов:

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

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

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

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

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

Ответы [ 11 ]

20 голосов
/ 03 октября 2008

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

12 голосов
/ 03 октября 2008

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

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

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

5 голосов
/ 03 февраля 2009

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

4 голосов
/ 03 октября 2008

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

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

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

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

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

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

3 голосов
/ 04 октября 2008

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

2 голосов
/ 03 октября 2008

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

1 голос
/ 03 октября 2008

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

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

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

1 голос
/ 03 октября 2008

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

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

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

0 голосов
/ 03 октября 2008

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

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

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