Оптимизирует ли .NET JIT вложенные операторы try / catch? - PullRequest
0 голосов
/ 10 июля 2009

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

Ответы [ 2 ]

8 голосов
/ 10 июля 2009

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

4 голосов
/ 13 июля 2009

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

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

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

...