Я думал о вложенных операторах try / catch и начал думать о том, при каких условиях JIT может выполнить оптимизацию или упрощение скомпилированного IL.
Для иллюстрации рассмотрим следующие функционально эквивалентные представления обработчика исключений.
// Nested try/catch
try
{
try
{
try
{
foo();
}
catch(ExceptionTypeA) { }
}
catch(ExceptionTypeB) { }
}
catch(ExceptionTypeC) { }
// Linear try/catch
try
{
foo();
}
catch(ExceptionTypeA) { }
catch(ExceptionTypeB) { }
catch(ExceptionTypeC) { }
Предполагая, что в кадрах стека вложенного оператора try нет дополнительных ссылок на переменные или вызовов функций, может ли JIT заключить, что кадры стека могут быть свернуты до линейного примера?
А как насчет следующего примера?
void Try<TException>(Action action)
{
try
{
action();
}
catch (TException) { }
}
void Main()
{
Try<ExceptionC>(Try<ExceptionB>(Try<ExceptionA>(foo)));
}
Я не думаю, что у JIT есть какой-либо способ встроить вызовы делегата, поэтому этот пример не может быть сведен к предыдущему. Однако, если foo()
выбросить ExceptionC
, будет ли это решение хуже по сравнению с линейным примером? Я подозреваю, что есть дополнительные затраты на удаление кадров стека из вызовов делегата, даже если дополнительные данные, содержащиеся в кадрах, минимальны.