Вопрос

Я думал о вложенных операторах 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, работает ли это решение хуже по сравнению с линейным примером?Я подозреваю, что удаление фреймов стека из вызовов делегатов требует дополнительных затрат, даже несмотря на то, что дополнительные данные, содержащиеся в фреймах, минимальны.

Это было полезно?

Решение

Стоит отметить, что в первом случае они Только функционально эквивалентно, когда вы ничего не делаете в блоке catch.В противном случае, рассмотрите это:

try
{
    foo();
}
catch (IOException)
{
    throw new ArgumentException(); // Bubbles up to caller
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

против

try
{
    try
    {
        foo();
    }
    catch (IOException)
    {
        throw new ArgumentException(); // Caught by other handler
    }
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

Теперь, в этом случае разница очевидна, но если блок catch вызывает какой-то произвольный метод, как JIT должен знать, что может быть выброшено?Лучше быть осторожным.

Это оставляет нам возможность JIT выполнять оптимизацию для пустых блоков catch - практика, которая в первую очередь настоятельно не рекомендуется.Я не хочу, чтобы JIT тратил время, пытаясь обнаружить неправильный код и заставить его работать немного быстрее - если вообще есть какая-либо разница в производительности.

Другие советы

Мое понимание регионов try / catch / finally в отношении производительности заключается в том, что такие регионы прозрачны для регулярного выполнения кода.То есть, если ваш код не выдает никаких исключений для catch, то регионы try / catch / finally имеют НОЛЬ влияние на производительность выполнения кода.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top