Предположим я реализовал некоторый класс, у которого есть внутреннее поле значение которого не должно выходить за определённые пределы и мне нужно как то обработать во внешнем классе ситуацию когда это поле выходит за границы допустимого диапазона. Тут возможно два варианта. Либо я реализую в искомом классе исключение и обрабатываю его во внешнем, либо я создаю у класса поле возвращающее контролируемое значение и перед выполнением операции которая потенциально может вывести контролируемое значение за пределы допустимого диапазона, проверяю его.
Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
p/s
Исходим из того, что при проверке значения выполняется простая операция, типа больше ли одно число другого, а не сравнение классов.
Здравствуйте, Cynic, Вы писали:
C>Предположим я реализовал некоторый класс, у которого есть внутреннее поле значение которого не должно выходить за определённые пределы и мне нужно как то обработать во внешнем классе ситуацию когда это поле выходит за границы допустимого диапазона. Тут возможно два варианта. Либо я реализую в искомом классе исключение и обрабатываю его во внешнем, либо я создаю у класса поле возвращающее контролируемое значение и перед выполнением операции которая потенциально может вывести контролируемое значение за пределы допустимого диапазона, проверяю его. C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
C>p/s C>Исходим из того, что при проверке значения выполняется простая операция, типа больше ли одно число другого, а не сравнение классов.
Если использовать проектирование по контракту, должно быть и то и другое. Что бы это не было слишком накладным, есть смысл проверять вызовы только внешних систем к разрабатываемой. Подробнее тут: http://rsdn.ru/article/design/Code_Contracts.xml
Здравствуйте, Cynic, Вы писали:
C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
Достаточно затратно. Исключение в Windows всегда идет через переключение в режим ядра.
When an exception occurs, whether it is explicitly raised by software or implicitly raised by hardware, a chain of events begins in the kernel. The CPU hardware transfers control to the kernel trap handler, which creates a trap frame (as it does when an interrupt occurs). The trap frame allows the system to resume where it left off if the exception is resolved. The trap handler also creates an exception record that contains the reason for the exception and other pertinent information.
...
If the exception occurred in user mode, the exception dispatcher does something more elaborate. As you'll see in Chapter 6, the Win32 subsystem has a debugger port and an exception port to receive notification of user-mode exceptions in Win32 processes. The kernel uses these in its default exception handling, as illustrated in Figure 3-6.
Debugger breakpoints are common sources of exceptions. Therefore, the first action the exception dispatcher takes is to see whether the process that incurred the exception has an associated debugger process. If so, it sends the first-chance debug message (via an LPC port) to the debugger port associated with the process that incurred the exception. (The message is sent to the session manager process, which then dispatches it to the appropriate debugger process.)
If the process has no debugger process attached, or if the debugger doesn't handle the exception, the exception dispatcher switches into user mode and calls a routine to find a frame-based exception handler. If none is found, or if none handles the exception, the exception dispatcher switches back into kernel mode and calls the debugger again to allow the user to do more debugging. (This is called the second-chance notification.)
All Win32 threads have an exception handler declared at the top of the stack that processes unhandled exceptions. This exception handler is declared in the internal Win32 start-of-process or start-of-thread function. The start-of-process function runs when the first thread in a process begins execution. It calls the main entry point in the image. The start-of-thread function runs when a user creates additional threads. It calls the user-supplied thread start routine specified in the CreateThread call.
(C) Соломон-Руссинович, Внутреннее устройство Windows
Здравствуйте, Cynic, Вы писали:
C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
А попробовать самое очевидное — написать тестовый пример и сравнить — не?
Здравствуйте, Cynic, Вы писали:
C>Предположим я реализовал некоторый класс, у которого есть внутреннее поле значение которого не должно выходить за определённые пределы и мне нужно как то обработать во внешнем классе ситуацию когда это поле выходит за границы допустимого диапазона. Тут возможно два варианта. Либо я реализую в искомом классе исключение и обрабатываю его во внешнем, либо я создаю у класса поле возвращающее контролируемое значение и перед выполнением операции которая потенциально может вывести контролируемое значение за пределы допустимого диапазона, проверяю его. C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
C>p/s C>Исходим из того, что при проверке значения выполняется простая операция, типа больше ли одно число другого, а не сравнение классов.
Использование исключений — затратно. не так, как SEH, но затратно.[/*]
Тем не менее, кидать исключение — Design-by-default для .Net{/*]
Отсюда вывод: Все сильно зависит от использования библиотеки (класса). Если библиотека разработана для внутренних нужд — используй флажки или еще что-то. Если библиотека МОЖЕТ быть использована где-то в другом месте — лучше использовать исключения.
Как компромиссное решение — сделай так, как было в свое время сделано в Managed DirectX — есть флажок, кидаться ли исключениями — вот и все. Сделай такой флажок, по умолчанию выставь в true, а там, где сам работаешь с библиотекой — можешь отключать и проверять валидность значений в другом месте...
P.S. свое исключение лучше не пиши — используй стандартное ArgumentOutOfRangeException.
Если при компиляции и исполнении вашей программы не происходит ни одной ошибки — это ошибка компилятора :)))
Здравствуйте, Cynic, Вы писали:
C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
Все относительно. Если исключений будут миллионы, то затратно. Там ведь и объект создается, и стек раскручивается.
Но в принципе исключение это довольно быстро. В релизе и без отладки можно выбрасывать миллион исключений в секунду.
А вот под отладкой первое исключение будет втыкать, так как в этом момент просыпается отладчик и что-то там думает.
Вообще, надо стараться не делать логику на исключениях. Любое правило не без исключений, но стараться надо. Если реализация без исключения выглядит сравнимо, то лучшее предпочесть ее. А исключения оставить для ошибок.
Хотя каюсь, сам иногда использую исключения для перехвата управления. Например, недавно писал автодополнение по ключевым словам в парсере. Самое просто оказалось сформировать списко и запихать его в исключение. Дальнейшая работа парсер в обычном режиме не имеет смысла, а протаскивать се через обычные возвращаемые значения просто неодъемно. Но сам понимаю, что хак.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Cynic, Вы писали:
C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
Если возник такой вопрос — вероятно, что-то не так с дизайном программы.
К примеру, у нас в проекте логика такая: если генерируется исключение, значит что-то не так либо с входными данными,
либо с системой. В обоих случаях сообщаем пользователю о проблеме и ждем его действий.
Соответственно, тут уже не важно, займет отработка такой ситуации 10 тактов процессора или 10000.
А вот сам процесс проверок — да, должен быть очень быстрым.
Тут правило из Макконнелла — в публичных методах делаем полную проверку входных данных, в защищённых — используем ассёрты.
Re[3]: На сколько затратно выбрасывание исключения
Здравствуйте, drol, Вы писали:
D>И причём здесь SEH, когда речь идёт об обычных исключениях внутри .NET ?
"Обычные" исключения выбрасываются с помощью вызова Win API RaiseException.
Вот здесь рассмотрение того, что происходит в дотнете при throw из C#. Первый и второй CallStack дают исчерпывающую информацию. throw из дотнета приводит в конечном счете к вызову нативной RaiseException из KERNELBASE.dll, а она приводит к переключению в режим ядра и далее, как в моем первом сообщении в этом топике.
Здравствуйте, Cynic, Вы писали: C>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
Простой, понятный, неправильный подход:
Запускаем, смотрим на результаты.
Код
using System;
using System.Diagnostics;
public class Program
{
static void Main()
{
const int Count = 100 * 1000;
Measure("Fast, callstack 0", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeFast(0);
}
return Count;
});
Measure("Exc, callstack 0", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeExc(0);
}
return Count;
});
Measure("Fast, callstack 1", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeFast(1);
}
return Count;
});
Measure("Exc, callstack 1", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeExc(1);
}
return Count;
});
Measure("Fast, callstack 10", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeFast(10);
}
return Count;
});
Measure("Exc, callstack 10", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeExc(10);
}
return Count;
});
Measure("Fast, callstack 20", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeFast(20);
}
return Count;
});
Measure("Exc, callstack 20", () =>
{
for (int i = 0; i < Count; i++)
{
TryCallMeExc(20);
}
return Count;
});
Console.Write("Done.");
Console.ReadKey();
}
static bool TryCallMeFast(int count)
{
return TryCallMeCore(count, false);
}
static bool TryCallMeExc(int count)
{
try
{
return TryCallMeCore(count, true);
}
catch
{
return false;
}
}
const int CatchEvery = 5;
static bool TryCallMeCore(int count, bool fail)
{
if (count < 0) throw new ArgumentOutOfRangeException("Dont be kiddy.");
if (count == 0)
{
if (fail) throw new InvalidOperationException("As you wish.");
return false;
}
if (count % CatchEvery == 1)
{
try
{
return TryCallMeCore(count - 1, fail);
}
catch
{
throw;
}
}
else
{
return TryCallMeCore(count - 1, fail);
}
}
static void Measure(string name, Func<long> callback)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
var sw = Stopwatch.StartNew();
var tmp = callback();
sw.Stop();
Console.WriteLine("{0,30}: {1,5}ms, ips: {2,16:F4} : {3,9}", name, sw.ElapsedMilliseconds, tmp / sw.Elapsed.TotalSeconds, tmp);
}
}
Понимаем, что с "миллионом исключений в секунду" VladD2 явно погорячился
Do not use exceptions for normal flow of control. Except for system failures, there should generally be a way to write code that avoids exceptions being thrown.
Re[2]: На сколько затратно выбрасывание исключения
Здравствуйте, btn1, Вы писали:
B>Здравствуйте, Cynic, Вы писали:
C>>Вопрос в том, на сколько затратно выбрасывание исключение, по сравнению с проверкой значения поля перед выполнением операции?
B>А попробовать самое очевидное — написать тестовый пример и сравнить — не?
Как бы сравнить то я могу, а вот получить такого исчерпывающего ответа который дал Павел Дворкин выше, нет.
:)
Re[2]: На сколько затратно выбрасывание исключения
Вы всё отлично пояснили, вот только хотелось бы более конкретную рекомендацию услышать.
Например возьмём такой пример: Есть у меня есть некий буфер, который может периодически переполняться и переполнение нужно как-то обрабатывать. Я могу решить эту проблему двумя путями:
Выбросить исключение в коде буфера и обработать во внешнем коде
Всякий раз при добавлении элемента проверять есть ли в буфере место
При этом операции записи в буфер происходят довольно часто. Правильно ли я понимаю, что в данном случае лучше избегать исключений?
:)
Re[3]: На сколько затратно выбрасывание исключения
Здравствуйте, Cynic, Вы писали:
C>При этом операции записи в буфер происходят довольно часто. Правильно ли я понимаю, что в данном случае лучше избегать исключений?
Неправильно При попытке записать мимо буфера в любом случае должно бросаться исключение. Иначе вы останетесь с испорченными данными и никак об этом не узнаете.
Если буфер — простой массив, то исключение бросят за вас, достаточно обернуть его в custom type exception (если требуется).
Если соблюдаются все три условия:
* попытки записать мимо буфера будут происходить постоянно;
* это нормальная ситуация;
* вызывающий код знает, как такие моменты обрабатывать.
(я что-то не могу себе такой сценарий представить, но пусть будет),
то: рядом с методом Write надо добавить TryWrite, который в случае облома не будет писать в буфер и просто вернёт false.
Прочитайте ссылки из моего ответа выше, там всё детально разжёвано.
Re[4]: На сколько затратно выбрасывание исключения
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Cynic, Вы писали:
C>>При этом операции записи в буфер происходят довольно часто. Правильно ли я понимаю, что в данном случае лучше избегать исключений? S>Неправильно При попытке записать мимо буфера в любом случае должно бросаться исключение. Иначе вы останетесь с испорченными данными и никак об этом не узнаете.
S>Если буфер — простой массив, то исключение бросят за вас, достаточно обернуть его в custom type exception (если требуется).
S>Если соблюдаются все три условия: S>* попытки записать мимо буфера будут происходить постоянно; S>* это нормальная ситуация; S>* вызывающий код знает, как такие моменты обрабатывать.
S>(я что-то не могу себе такой сценарий представить, но пусть будет), S>то: рядом с методом Write надо добавить TryWrite, который в случае облома не будет писать в буфер и просто вернёт false.
S>Прочитайте ссылки из моего ответа выше, там всё детально разжёвано.
Да речь только о производительности решения. Я могу обрабатывать исключение, а могу проверять доступное место. Вопрос в том, что более производительно.
:)
Re[5]: На сколько затратно выбрасывание исключения
Здравствуйте, Cynic, Вы писали:
C>Да речь только о производительности решения. Я могу обрабатывать исключение, а могу проверять доступное место. Вопрос в том, что более производительно.
И ещё раз см. мой первый ответ. Там и про производительность, и про "как оно правильно решается".
Re[3]: На сколько затратно выбрасывание исключения
Здравствуйте, Cynic, Вы писали:
C>Например возьмём такой пример: Есть у меня есть некий буфер, который может периодически переполняться и переполнение нужно как-то обрабатывать. Я могу решить эту проблему двумя путями: C>
C> Выбросить исключение в коде буфера и обработать во внешнем коде C> Всякий раз при добавлении элемента проверять есть ли в буфере место C>C>При этом операции записи в буфер происходят довольно часто. Правильно ли я понимаю, что в данном случае лучше избегать исключений?
Что означает "буфер может переполняться" ? Мы же в управляемом коде. Если это массив и выйти за его границы, то ArrayIndexOutOfBoundsException и так выбросится. Если же что-то иное — хотелось бы знать, что именно.
With best regards
Pavel Dvorkin
Re[4]: На сколько затратно выбрасывание исключения
Здравствуйте, Sinix, Вы писали:
S>Если соблюдаются все три условия: S>* попытки записать мимо буфера будут происходить постоянно; S>* это нормальная ситуация; S>* вызывающий код знает, как такие моменты обрабатывать.
S>(я что-то не могу себе такой сценарий представить, но пусть будет),
Такой сценарий можно себе представить, но не в дотнете. Впрочем, и в дотнете, наверное, можно, если включить unsafe код. Это "истинный" (то есть без реаллокаций) динамический массив.
Идея простая — резервируем под него на максимум. Скажем, на 512 Мб. А что — не жалко. Это же не выделение памяти, а только резервирование АП.
Коммитируем, скажем, 64 Кб в самом начале. Пока хватит.
Начинаем писать. Последовательно. Строки, например, одна за другой. Пока они в 64 Кб помещаются — нет проблем. Но мы не следим за тем, помещаются или нет, просто пишем.
Раньше или позже выйдем за пределы 64 Кб. Словим exception, обработаем его : коммитируем еще 64 Кб, повторим запись. И т.д.
Естественно, предполагаю, что записываемая строка длиной <64 Кб. Если не так — будет чуть сложнее, но принцип не изменится
Другой похожий пример — огромная сильно разреженная матрица. В этом случае резервируем на весь ее размер, и не коммитируем ничего. Сразу пишем, сразу exception, обрабатываем его — коммитируем 4 Кб (меньше нельзя, страница). При следующей записи либо попадем на уже выделенную страницу, либо опять exception и т.д.
Для первой задачи, впрочем, вполне можно (и ИМХО стоит) обойтись все же без исключений. Несложно же проверять при последовательной записи, хватит оставшегося места или нет, и если не хватит — добавить 64 Кб.
Для второй задачи тоже, конечно, можно без исключенйи обойтись, но придется список выделенных блоков иметь и по нему пробегать, что приведет O(1) к чему-то мало приличному.
Первую задачу я студентам даю в качестве упражнения
With best regards
Pavel Dvorkin
Re[4]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>throw из дотнета приводит в конечном счете к вызову нативной RaiseException из KERNELBASE.dll, а она приводит к переключению в режим ядра
Кто Вам это сказал ??? RaiseException совершенно не обязана переключаться в kernel mode. В случае 64-битного процессаRaiseException уходит в ядро только при подключенном отладчике, необработанном исключении и тому подобных ситуациях. Обычные же исключения обрабатываются полностью в user mode... Ну или мне счётчики kernel\user time врут...
Re[5]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
S>>(я что-то не могу себе такой сценарий представить, но пусть будет), PD>Такой сценарий можно себе представить, но не в дотнете. Впрочем, и в дотнете, наверное, можно, если включить unsafe код. Это "истинный" (то есть без реаллокаций) динамический массив.
Не, это уже совсем частный случай. Топикстартер, думаю, оговорил бы этот момент.
Re[5]: На сколько затратно выбрасывание исключения
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>throw из дотнета приводит в конечном счете к вызову нативной RaiseException из KERNELBASE.dll, а она приводит к переключению в режим ядра
D>Кто Вам это сказал ??? RaiseException совершенно не обязана переключаться в kernel mode. В случае 64-битного процессаRaiseException уходит в ядро только при подключенном отладчике, необработанном исключении и тому подобных ситуациях. Обычные же исключения обрабатываются полностью в user mode... Ну или мне счётчики kernel\user time врут...
Я не исключаю, что в x64 что-то изменилось, но хотелось бы более серьезного доказательства, нежели данные счетчиков времени. Если такие доказательства будут, я охотно поменяю свое мнение в том, что касается x64.
Пока что могу лишь сказать, что NtRaiseException как сервис ядра в x64 существует. Вот здесь ее номер даже (в смысле syscall) приведен.
Правда, я не совсем понимаю, что Вы имеете в виду, говоря о необработанном исключении. RaiseException его выбрасывает, а обработка будет потом, если будет. Получается, что если обработки не будет, то управление вернется обратно в RaiseException, чтобы уйти в kernel ? Как-то странно это. Или она сама и должна заниматься обработкой исключения ? Так не ее же это дело, исключения могут и без нее возникнуть вполне. Что-то я не понимаю.
With best regards
Pavel Dvorkin
Re[6]: На сколько затратно выбрасывание исключения
Хм, а как вот это можно объяснить?
Ведь и в том и в другом случае, судя по коду, кидается 1 исключение, но при этом время увеличивается почти в 2 раза?
Re[6]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>но хотелось бы более серьезного доказательства,
Я с большим интересом послушаю эти самые Ваши доказательства. Вы ведь, надеюсь, не забыли, что тезис о переходах в ядро при обычной обработке обычных исключений принадлежит Вам ?
PD>нежели данные счетчиков времени
Лично меня они пока вполне устраивают. Разница с 32-битным процессом WoW64 впечатляет более чем...
PD>Пока что могу лишь сказать, что NtRaiseException как сервис ядра в x64 существует. Вот здесь ее номер даже (в смысле syscall) приведен.
И причём здесь NtRaiseException\ZwRaiseException В приведённом Вами стеке вызовов их нет. Там только RaiseException и RtlRaiseException. Вам напомнить, что означает префикс Rtl ?
Итого, Вы пока не показали вообще ни одного факта перехода в режим ядра
PD>Правда, я не совсем понимаю, что Вы имеете в виду, говоря о необработанном исключении. RaiseException его выбрасывает, а обработка будет потом, если будет
Приехали. А что такое по-Вашему вообще "выбрасывание" ? Сакральный магический акт ?
PD>Получается, что если обработки не будет, то управление вернется обратно в RaiseException,
И когда обработка есть оно туда тоже возвращается. Причём n раз (в данном примере SEH + куча вложенных __finally\__except):
*И эти люди преподают программирование на C\C++
PD>Или она сама и должна заниматься обработкой исключения ?
Совершенно верно...
*Понятно, что не собственно RaiseException, а то что вызывается внутри неё. Основная логика находится в RtlRaiseException и в RtlDispatchException.
PD>Так не ее же это дело, исключения могут и без нее возникнуть вполне.
Что значит "без неё" Нормальное исключение это и есть вызов кого-то из xxxRaiseException. Для "ядерных приветов" имеются оптимизации, которые передают управление сразу на уровень KiUserExceptionDispatch.
PD>Что-то я не понимаю.
Вы очень много чего не понимаете...
Re[5]: На сколько затратно выбрасывание исключения
A>Хм, а как вот это можно объяснить? A>Ведь и в том и в другом случае, судя по коду, кидается 1 исключение, но при этом время увеличивается почти в 2 раза?
Да легко. Для "пустого" стека вызовов сделана какая-нибудь оптимизация по данным. А для размера даже в 1 уже кучу выделений памяти в куче надо делать, например.
Re[4]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Что означает "буфер может переполняться" ? Мы же в управляемом коде. Если это массив и выйти за его границы, то ArrayIndexOutOfBoundsException и так выбросится. Если же что-то иное — хотелось бы знать, что именно.
Буфер это список (List<T>) который имеет ограниченное количество элементов. Надо как-то обработать ситуацию когда использующий его код хочет добавить элемент в буфер когда он уже заполнен.
:)
Re[2]: На сколько затратно выбрасывание исключения
S>Do not use exceptions for normal flow of control. Except for system failures, there should generally be a way to write code that avoids exceptions being thrown.
В общем после прочтения указанных ссылок я сделал вывод, что если речь идет о производительности, то использования исключений нужно избегать. А в остальных случаях все зависит от ситуации.
:)
Re[6]: На сколько затратно выбрасывание исключения
Здравствуйте, drol, Вы писали:
D>Да легко. Для "пустого" стека вызовов сделана какая-нибудь оптимизация по данным. А для размера даже в 1 уже кучу выделений памяти в куче надо делать, например.
Там всё проще, +1 catch-throw
Здравствуйте, drol, Вы писали:
D>Я с большим интересом послушаю эти самые Ваши доказательства. Вы ведь, надеюсь, не забыли, что тезис о переходах в ядро при обычной обработке обычных исключений принадлежит Вам ?
Пожалуйста. Первый рисунок демонстрирует схему выбрасывания и обработки исключения.
D>Итого, Вы пока не показали вообще ни одного факта перехода в режим ядра
См. выше.
PD>>Так не ее же это дело, исключения могут и без нее возникнуть вполне.
D>Что значит "без неё" Нормальное исключение это и есть вызов кого-то из xxxRaiseException.
Именно это и значит. При делении на 0 или access violation происходит исключение процессора, управление передается в ядро минуя вызов RaiseException в user mode. Именно о ней я и писал. Что при этом вызывается в ядре — я не обсуждал.
D>Вы очень много чего не понимаете...
Вынужден на этом дискуссию с Вами прекратить. Я не веду дискуссию с людьми, позволяющими себе агрессивный тон и переход на личности.
With best regards
Pavel Dvorkin
Re[5]: На сколько затратно выбрасывание исключения
Здравствуйте, Cynic, Вы писали:
PD>>Что означает "буфер может переполняться" ? Мы же в управляемом коде. Если это массив и выйти за его границы, то ArrayIndexOutOfBoundsException и так выбросится. Если же что-то иное — хотелось бы знать, что именно.
C>Буфер это список (List<T>) который имеет ограниченное количество элементов. Надо как-то обработать ситуацию когда использующий его код хочет добавить элемент в буфер когда он уже заполнен.
Если это и впрямь System.Collections.Generiс.List, то количество его элементов растет автоматически
If Count already equals Capacity, the capacity of the List<T> is increased by automatically reallocating the internal array, and the existing elements are copied to the new array before the new element is added.
Так что пока не будет OutOfMemoryException — добавляй и ни о чем не думай
With best regards
Pavel Dvorkin
Re[5]: На сколько затратно выбрасывание исключения
Здравствуйте, Cynic, Вы писали:
C>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Что означает "буфер может переполняться" ? Мы же в управляемом коде. Если это массив и выйти за его границы, то ArrayIndexOutOfBoundsException и так выбросится. Если же что-то иное — хотелось бы знать, что именно.
C>Буфер это список (List<T>) который имеет ограниченное количество элементов. Надо как-то обработать ситуацию когда использующий его код хочет добавить элемент в буфер когда он уже заполнен.
Т.е. ограниченность буфера — элемент бизнес-логики? Может, вам что-нибудь вроде циклического буфера подойдет? И почему нельзя просто игнорировать вызов добавления, к примеру?
Re[8]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Пожалуйста. Первый рисунок демонстрирует схему выбрасывания и обработки исключения.
И зачем Вы приводите схему десятилетней давности для 32-битных процессов, когда речь уже давно идёт о 64-битных
D>>Что значит "без неё" Нормальное исключение это и есть вызов кого-то из xxxRaiseException.
PD>Именно это и значит. При делении на 0 или access violation происходит исключение процессора, управление передается в ядро минуя вызов RaiseException в user mode
Зачем Вы разговариваете сам с собой ? Было же написано нормальным английско-программистским языком: xxxRaiseException.
D>>Вы очень много чего не понимаете...
PD>Вынужден на этом дискуссию с Вами прекратить. Я не веду дискуссию с людьми, позволяющими себе агрессивный тон и переход на личности.
Странная какая-то у Вас реакция. Вот мне бы на Вашем месте было очень стыдно, что давно не в теме, а студентам втираю. И чтобы как можно быстрее устранить пробелы в своих знаниях я бы использовал все доступные возможности... Жаль Ваших студентов — совсем не повезло им с преподавателем...
Re[7]: На сколько затратно выбрасывание исключения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Честно говоря, я не ждал, что будет настолько хуже.
Да ненамного хуже SEH вроде
А всё просто: исключения в дотнете заточены под исключительные ситуации (сорри за фиговый каламбур). Если производительность утыкается в стоимость обработки исключений, то проблема уже на 100% не в них самих, а в непродуманной архитектуре / незнании матчасти / неправильно написанном API и тд и тп. Исключений из этого правила на практике не видел.
Ну и разумеется в крайности не надо впадать. Исключения отлично работают для отмены/отката бизнес-операций (кучу понятных оговорок про необходимость отката состояния, побочные эффекты и тд и тп пропустим). А вот писать try-catch вокруг метода проверки заказа — это уже перебор
P.S. Сорри за передоз слова "исключение". Это исключительная ситуация.
Тут все сильно зависит. Если отталкиваться от теории, то исключение это нарушение состояния типа(класса): предусловие, пост условие и инвариант, другими словами класс не может выполнить декларируемый контрактом функционал.
Если у вас нет каких-то узкоспециализированных "но", то надо бросать исключение, потому как код обработки в текущем вызове может не знать, а скорее всего и не должен знать что ему делать если произошел выход за пределы. А вот тот кто проверяет, может знать что ему делать, если произошел выход за пределы, и в этом случае целесообразно расширить интерфейс типа и добавить возможность проверки. Обычно такой подход используется в бизнес логике.
Решать надо не со стороны производительности, а со стороны того, что лучше подходит — исключение или дополнительная проверка (когда, где и как использовать исключения — это отдельная большая тема). Если у вас в цикле не выбрасываются тысячи исключений в секунду, то на производительность надо смотреть в самую последнюю очередь (или скорее вообще не смотреть, исключения не настолько тормозные вещи, чтобы их бояться и даже тратить время на то, чтобы задумываться об их производительности). А если выбрасываются тысячи исключений, то надо критически посмотреть на дизайн такого решения.
Re[2]: На сколько затратно выбрасывание исключения
Здравствуйте, MozgC, Вы писали:
MC>Решать надо не со стороны производительности, а со стороны того, что лучше подходит — исключение или дополнительная проверка
Вы там сговорились, что ли?
Есть официальные гадлайны, что и когда использовать. В частности
Do not use exceptions for normal flow of control. Except for system failures, there should generally be a way to write code that avoids exceptions being thrown.
Если есть сомнения — проверяем бенчмарком. Он наглядно показывает, что рекомендации не на пустом месте написаны
А всё просто: исключения в дотнете заточены под исключительные ситуации (сорри за фиговый каламбур). Если производительность утыкается в стоимость обработки исключений, то проблема уже на 100% не в них самих, а в непродуманной архитектуре / незнании матчасти / неправильно написанном API и тд и тп. Исключений из этого правила на практике не видел.
Ну и разумеется в крайности не надо впадать. Исключения отлично работают для отмены/отката бизнес-операций (кучу понятных оговорок про необходимость отката состояния, побочные эффекты и тд и тп пропустим). А вот писать try-catch вокруг метода проверки заказа — это уже перебор
Ну и собственно вопрос: как можно прочитать рекомендацию "не делайте так" и прийти к прямо противоположным выводам типа:
В общем после прочтения указанных ссылок я сделал вывод, что если речь идет о производительности, то использования исключений нужно избегать. А в остальных случаях все зависит от ситуации.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, MozgC, Вы писали:
MC>>Решать надо не со стороны производительности, а со стороны того, что лучше подходит — исключение или дополнительная проверка
S>Есть официальные гадлайны, что и когда использовать. В частности
S>Do not use exceptions for normal flow of control. Except for system failures, there should generally be a way to write code that avoids exceptions being thrown.
А я где-то рекомендовал использовать исключения "for normal flow of control"?
S>Если есть сомнения — проверяем бенчмарком.
Сомнения могут появляться когда есть шанс, что исключение будет выкидываться с очень большой частотой. В противном случае, повторюсь, при принятии решения в первую очередь надо основываться на том, как будет лучше/правильнее в плане дизайна.
S>Он наглядно показывает, что рекомендации не на пустом месте написаны
ТС собирается это исключение выбрасывать тысячи раз в секунду? Если нет, то зачем считать микросекунды, по крайней мере пока не замечена проблема с производительностью?
S>Ну и собственно вопрос: как можно прочитать рекомендацию "не делайте так" и прийти к прямо противоположным выводам типа: S>
S>В общем после прочтения указанных ссылок я сделал вывод, что если речь идет о производительности, то использования исключений нужно избегать. А в остальных случаях все зависит от ситуации.
S>Решать надо не со стороны производительности, а со стороны того, что лучше подходит — исключение или дополнительная проверка
S>
Ты под рекомендацией "не делайте так" имеешь в виду не использовать исключения "for normal flow of control"? Если да, то я не вижу противоречия между этой рекомендацией и тем что я написал, т.к., повторюсь, я не рекомендовал использовать исключения для "normal flow of control".
S>Ладно один человек ошибается, но четыре? (отплюсовавшие vrr и Sharov, я вас тоже посчитал )
Ммм.. я пока не вижу, что я ошибаюсь
Re[4]: На сколько затратно выбрасывание исключения
MC>А я где-то рекомендовал использовать исключения "for normal flow of control"?
Ну, по крайней мере я так понял вот эту часть — "... со стороны того, что лучше подходит — исключение или дополнительная проверка".
Официальные рекомендации тут крайне однозначны: если в коде возникла ситуация, которую код не знает, как обрабатывать, или если контракт вызова нарушен — бросай исключение.
На случай, если вызывающий код знает, как эту ситуацию обрабатывать есть стандартные tester-doer или TryParse.
Собственно, это всё, что надо знать для выбора "бросать-не-бросать". Разумеется, есть исключения, но они строго индивидуальны и всегда начинаются с конкретного сценария использования. Из практики я могу вспомнить только один рабочий хак, ради которого имеет смысл отступать от правила выше: откат бизнес-операций.
Любой бизнес-код по определению должен уметь откатывать изменения до согласованного состояния, если в ходе выполнения произошла ошибка. Если это правило не соблюдается, то у клиента рано или поздно поизойдёт страшное(тм) и это страшное будет исправить гораздо дороже, чем подумать о корректной обработке исключений с самого начала.
Ну а дальше следует очевидное: раз мы потратили кучу усилий на соблюдение атомарности биз-операций, то грех не использовать готовый код для обработки бизнес-исключений. Например, в ходе оформления заказа внезапно выясняется, что выбранные пользователем товары нельзя экспортировать в страну назнчения. Конечно, можно нагородить фреймворк для валидации бизнес-правил или "холостой прогон" бизнес операции с последующей проверкой корректности и коммитом.
Иногда это даже будет оправданно, но в большинстве случаев достаточно бросить BusinessException(Resources.XyzMessage) и не заморвчиваться.
S>>Если есть сомнения — проверяем бенчмарком. MC>Сомнения могут появляться когда есть шанс, что исключение будет выкидываться с очень большой частотой.
Бенчмарк нужен в любом варианте, потому что у всех понятие "большая частота" разное. Выше по ветке Влад закладывался на миллионы, у тебя — тысячи. На практике даже сотня исключений в секунду на нагруженном сервисе может очень здорово нагадить.
Но! это не значит, что проверки с исключениями надо убирать из кода, "так как производительность". Иначе рано или поздно получим мой сегодняшний кейс:
cache.TryGet(objKey, out obj);
obj.DoSomething();
Автор API хотел как лучше и не предоставил метода, бросающего исключение. В принципе, когда-то это было оправданно, т.к. метод использовался в ограниченном количестве мест. Но к моменту, когда ошибка всплыла, код успел разползтись по нескольким классам, местами породив комбайны для протаскивания сигнала об отмене обратно по стеку. И чтобы было совсем весело, другая добрая душа "починила" код, возвращая new Obj() в случае промаха мимо кэша
В общем, смотрим оф. рекомендации, любое отступление начинаем с реального сценария использования, получившееся API документируем тестами/ассертами (которые суть те же исключения). Иначе никак
MC>ТС собирается это исключение выбрасывать тысячи раз в секунду?
Именно, выше по ветке было:
Cynic:
Например возьмём такой пример: Есть у меня есть некий буфер, который может периодически переполняться и переполнение нужно как-то обрабатывать. Я могу решить эту проблему двумя путями:
Выбросить исключение в коде буфера и обработать во внешнем коде
Всякий раз при добавлении элемента проверять есть ли в буфере место
При этом операции записи в буфер происходят довольно часто. Правильно ли я понимаю, что в данном случае лучше избегать исключений?
Я кажется понял, в чём проблема: ты не просматривал весь топик, а я отвечал именно в контексте ветки, где уже было несколько ответов в духе "раз исключения тормозят, то можно их не бросать"
Как всегда короче
Re[2]: На сколько затратно выбрасывание исключения