Подтвердить что ты не робот

Почему для оптимизации хвостового вызова нужен код op?

Итак Я читал много раз до, что технически .NET поддерживает оптимизацию хвостовых вызовов (TCO), потому что для нее есть код операции, а просто С# t генерирует его.

Я не совсем уверен, зачем TCO нужен код операции или что он будет делать. Насколько мне известно, требование о возможности TCO состоит в том, что результаты рекурсивного вызова не объединены с какими-либо переменными в текущей области функций. Если у вас этого нет, я не вижу, как код операции мешает вам открывать фрейм стека. Если у вас это есть, то не может ли компилятор легко скомпилировать его на что-то итеративное?

Итак, в чем смысл кода операции? Очевидно, там что-то мне не хватает. В случаях, когда TCO возможно вообще, не всегда ли можно обрабатывать его на уровне компилятора, чем на уровне кода операции? Какой пример того, где он не может?

4b9b3361

Ответ 1

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


Источник

<Р → CLR и хвосты звонят

Когда вы работаете с языками, управляемыми CLR, в игре есть два вида компиляторов. Там компилятор, который идет от вашего исходного кода до IL (разработчики С# знают это как csc.exe), а затем компилятор, который идет от IL к собственному коду (компиляторы JIT 32/64 бит, которые вызывают во время выполнения или время NGEN). Исходные компиляторы source- > IL и IL- > понимают оптимизацию хвостового вызова. Но собственный компилятор IL- > , который я буду называть JIT, имеет последнее слово в будет ли в конечном итоге использоваться оптимизация вызова хвоста. Компилятор source- > IL может помочь генерировать ИЛ, что способствует созданию хвостовых вызовов, включая использование "хвоста". IL prefix (подробнее об этом позже). Таким образом, компилятор source- > IL может структурировать IL, который он генерирует, чтобы убедить JIT в создании хвостового вызова. Но JIT всегда имеет возможность делать все, что захочет.

Когда JIT делает хвостовые звонки?

Я спросил Фей Чена и Гранта Ричинса, соседей по коридору от меня, которые, случается, работают над JIT, при каких условиях различные JIT будут использовать оптимизацию хвостового вызова. Полный ответ довольно подробный. Краткое краткое изложение состоит в том, что JITs пытаются использовать оптимизацию хвостового вызова всякий раз, когда могут, но есть много причин, по которым оптимизация хвостовых вызовов не может быть использована. Некоторые причины, по которым вызов хвоста не является опцией:

  • Caller не возвращается сразу после вызова (duh: -))
  • Аргументы стека между вызывающим и вызываемым несовместимы таким образом, что потребовалось бы перемещение вещей вокруг кадра вызывающего абонента до того, как вызываемый мог выполнить
  • Caller и callle возвращают разные типы.
  • Вместо этого мы встраиваем вызов (вставка лучше, чем вызов хвоста, и открывает двери для многих других оптимизаций)
  • Безопасность мешает
  • Отладчик/профилировщик отключил оптимизацию JIT

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

Безопасность в .NET во многих случаях зависит от точности стека... во время выполнения. Вот почему, как указано выше, бремя разделяется обоими источниками на компилятор CIL и (исполняемый файл) CIL-to-native JIT-компиляторы, причем последнее слово принадлежит последнему.

Ответ 2

Угадайте: на простом языке, таком как ассемблер x86, где вы управляете стеком "вручную", вам не нужен код операции - вы можете просто настроить стек вызовов соответствующим образом.

Но в чем-то более высоком уровне, таком как .NET CIL, для вас частично управляется стек, и весь акт вызова функции один код операции (например, вызов). Таким образом, вам нужен другой код операции для реализации TCO - тот, который "передает поток управления этой функции, но без создания нового фрейма стека".