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

Когда метод, подходящий для включения в CLR?

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

JIT wint inline:

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

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

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

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

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

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

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

Таким образом, эвристика, используемая компилятором X86 JIT, имеет встроенный кандидат.

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

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

  • Вычислить множитель. По умолчанию это 1

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

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

  • Если InlineSize <= NonInlineSize * Множитель выполняет inlining.

Ответ 3

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

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

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

Ответ 4

Там больше информации о inlining MethodBase.GetCurrentMethod в этом потоке http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html

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

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