Когда метод может быть встроен в CLR? - PullRequest
37 голосов
/ 11 января 2011

Я наблюдал много «интроспективного» стека кода в приложениях, которые часто неявно полагаются на их содержащие методы , а не , для их корректности.Такие методы обычно включают вызовы:

  • MethodBase.GetCurrentMethod
  • Assembly.GetCallingAssembly
  • Assembly.GetExecutingAssembly

Теперь я нахожуинформация, окружающая эти методы, должна быть очень запутанной.Я слышал, что во время выполнения не будет встроен метод, который вызывает GetCurrentMethod, но я не могу найти никакой документации на этот счет.Я видел сообщения в StackOverflow несколько раз, например, , этот , указывающий, что CLR не поддерживает встроенные вызовы кросс-сборки, но документация GetCallingAssembly строго указывает на иное.

Также есть много клеветнических [MethodImpl(MethodImplOptions.NoInlining)], но я не уверен, считает ли CLR это "запросом" или "командой".

Обратите внимание, что я спрашиваю о вставке приемлемость с точки зрения контракта, не о том, когда текущие реализации JITter отказываются рассматривать методы из-за трудностей с реализацией, или о том, когда JITter окончательно заканчивает , выбирая включить подходящий метод после оценки компромиссов.Я прочитал это и это , но они, кажется, более сфокусированы на последних двух моментах (здесь есть упоминания о MethodImpOptions.NoInlining и "экзотических инструкциях по IL", но они кажутсядолжен быть представлен как эвристика, а не как обязательства ).

Когда CLR разрешено встроить?

Ответы [ 5 ]

23 голосов
/ 11 января 2011

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

Встраивание методов из других сборок, безусловно, поддерживается, многие классы .NET будут работать совершенно безнадежно, если бы это было не так.Вы можете увидеть это на работе, когда посмотрите на машинный код, сгенерированный для Console.WriteLine (), он часто становится встроенным, когда вы передаете простую строку.Чтобы убедиться в этом, вам нужно переключиться на сборку Release и изменить опцию отладчика.Инструменты + Параметры, Отладка, Общие, снимите флажок «Подавить оптимизацию JIT при загрузке модуля».

В противном случае нет веских причин для рассмотрения MethodImpOptions.Фактически он намеренно используется в среде .NET для множества небольших открытых методов, которые вызывают внутренний вспомогательный метод.Это облегчает диагностику следов стека исключений.

6 голосов
/ 17 июля 2015

Несмотря на ответ Ханса Пассанта, здесь первые несколько подсказок по состоянию на 2004 год, а затем еще более свежая информация.Они могут быть изменены, но они дают вам представление о том, что искать, если вы хотите выбрать метод, пригодный для встраивания:

JIT не будет встроенным:

  • Методы, помеченные с помощью MethodImplOptions.NoInlining
  • Методы, превышающие 32 байта IL
  • Виртуальные методы
  • Методы, которые принимают тип большого значения в качестве параметра
  • Методы в классах MarshalByRef
  • Методы со сложными потоковыми диаграммами
  • Методы, отвечающие другим, более экзотическим критериям

В частности, существует MethodImplOptions.AggressiveInlining , который должен поднять ограничение в 32 байта (или что бы то ни было в наши дни и для вашей платформы).

.Net 3.5 добавил эвристику, которая помогает определить, Inline или нет Inline , что, вероятно, является хорошей вещью, хотя разработчику труднее предсказать решение о дрожании:

Цитата из статьи:

  1. Если вставка делает код меньше, чем вызов, который он заменяет, это ВСЕГДА хорошо.Обратите внимание, что речь идет о размере кода NATIVE, а не о размере кода IL (который может быть совершенно другим).

  2. Чем больше выполняется конкретный сайт вызовов, тем больше он извлекает выгоду из inlning.Таким образом, код в циклах заслуживает того, чтобы быть встроенным больше, чем код, который не находится в циклах.

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

    1. Оцените размер сайта вызова, если метод не был встроен.

    2. Оцените размер сайта вызоваесли бы он был встроенным (это оценка, основанная на IL, мы используем простой конечный автомат (модель Маркова), созданный с использованием большого количества реальных данных для формирования этой логики оценки)

    3. Computeмножитель.По умолчанию это 1

    4. Увеличить множитель, если код находится в цикле (текущая эвристика увеличивает его до 5 в цикле)

    5. Увеличьте множитель, если похоже, что оптимизация структуры вступит в силу.

    6. Если InlineSize <= NonInlineSize * Множитель делает вставку. </p>

3 голосов
/ 26 января 2011

Хотя ответ Ганса правильный, есть одно упущение, не обязательно о том, когда метод имеет право на вставку, но когда метод не .

Абстрактные и виртуальные методы не допускаются для встраивания в CLR .

Важно отметить, что он сокращает условия, при которых метод может быть встроенным.

1 голос
/ 09 июня 2012

Более подробная информация о вставке MethodBase.GetCurrentMethod в этой теме http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html

Сильно перефразируя, он заявляет, что RefCrawlMark НЕ останавливает встроенный вызывающий метод. Тем не менее RequireSecObject имеет побочный эффект от остановки вызова вызывающего абонента.

Кроме того, методы Assembly.GetCallingAssembly и Assembly.GetExecutingAssembly НЕ имеют этого атрибута.

0 голосов
/ 30 марта 2019

В 2003 году на MSDN была опубликована статья под названием Написание высокопроизводительных управляемых приложений , которая довольно четко описывает несколько критериев:

  • Методы, размер которых превышает 32 байта.IL не будет встроен.
  • Виртуальные функции не встроены.
  • Методы, которые имеют сложное управление потоком, не будут встроены.Комплексное управление потоком - это любое управление потоком, кроме if / then / else;в этом случае switch или while.
  • Методы, которые содержат блоки обработки исключений, не являются встроенными, хотя методы, которые генерируют исключения, все еще являются кандидатами для встраивания.
  • Если какой-либо из формальных аргументов метода является структурой, метод не будет встроен.

Статья Саша Гольдштейна в блоге в 2012 году о агрессивном встраивании в CLR имеет много того же совета.

...