Поскольку во время компиляции компилятор может создавать разные (может быть, немного) IL в режиме отладки и режиме выпуска, есть ли разница в IL, сгенерированном путем компиляции выражения при сборке в режиме отладки и режиме выпуска?
У этого на самом деле очень простой ответ: нет. При наличии двух идентичных деревьев выражений LINQ/DLR не будет никакой разницы в сгенерированном IL, если одно из них скомпилировано приложением, работающим в режиме выпуска, а другое — в режиме отладки. Я не уверен, как это будет реализовано в любом случае; Я не знаю никакого надежного способа, позволяющего коду в System.Core
узнать, что в вашем проекте выполняется отладочная сборка или выпускная сборка.
Однако этот ответ на самом деле может ввести в заблуждение. IL, выдаваемый компилятором выражений, может не отличаться между сборками отладки и выпуска, но в случаях, когда деревья выражений выдаются компилятором C#, возможно, что структура самих деревьев выражений может различаться в режимах отладки и выпуска. Я достаточно хорошо знаком с внутренностями LINQ/DLR, но не так хорошо знаком с компилятором C#, поэтому могу только сказать, что в этих случаях может быть разница (а может и не быть).
Кроме того, JIT, который преобразует IL в собственный код во время выполнения, должен сильно различаться как в режиме отладки, так и в режиме выпуска. Это также относится к скомпилированным выражениям? Или IL из деревьев выражений вообще не перегружаются?
Машинный код, который выдает JIT-компилятор, не обязательно будет сильно отличаться для предварительно оптимизированного IL и неоптимизированного IL. Результаты вполне могут быть идентичными, особенно если единственными отличиями являются несколько дополнительных временных значений. Я подозреваю, что они будут больше расходиться в более крупных и сложных методах, поскольку обычно существует верхний предел времени / усилий, которые JIT тратит на оптимизацию данного метода. Но похоже, вас больше интересует, как качество скомпилированных деревьев выражений LINQ/DLR сравнивается, скажем, с кодом C#, скомпилированным в режиме отладки или выпуска.
Я могу сказать вам, что LINQ/DLR LambdaCompiler
выполняет очень мало оптимизаций — уж точно меньше, чем компилятор C# в режиме Release; Режим отладки может быть ближе, но я бы поставил на то, что компилятор C# немного более агрессивен. LambdaCompiler
обычно не пытается сократить использование временных локальных переменных, а такие операции, как условные операторы, сравнения и преобразования типов, обычно используют больше промежуточных локальных переменных, чем можно было бы ожидать. На самом деле я могу представить только три оптимизации, которые он действительно выполняет:
Вложенные лямбда-выражения будут встроены, когда это возможно (и «когда это возможно», как правило, «большую часть времени»). На самом деле это может очень помочь. Обратите внимание, это работает только тогда, когда вы Invoke
a LambdaExpression
; он не применяется, если вы вызываете скомпилированный делегат в своем выражении.
Ненужные/избыточные преобразования типов опускаются, по крайней мере, в некоторых случаях.
Если значение TypeBinaryExpression
(т. е. [value] is [Type]
) известно во время компиляции, это значение может быть встроено как константа.
Помимо № 3, компилятор выражений не выполняет оптимизаций, основанных на выражениях; то есть он не будет анализировать дерево выражений в поисках возможностей оптимизации. Другие оптимизации в списке происходят практически без контекста других выражений в дереве.
Как правило, следует исходить из того, что IL, полученный в результате скомпилированного выражения LINQ/DLR, значительно менее оптимизирован, чем IL, созданный компилятором C#. Однако получившийся IL-код пригоден для JIT-оптимизации, поэтому трудно оценить реальное влияние на производительность, если вы не попытаетесь измерить его с помощью эквивалентного кода.
При составлении кода с деревьями выражений следует помнить, что вы фактически являетесь компилятором1. Деревья LINQ/DLR предназначены для генерации какой-либо другой инфраструктурой компилятора, например различными реализациями языка DLR. Таким образом, вы должны заниматься оптимизацией на уровне выражений. Если вы небрежный компилятор и создаете кучу ненужного или избыточного кода, сгенерированный IL будет больше и с меньшей вероятностью будет агрессивно оптимизирован JIT-компилятором. Так что помните о выражениях, которые вы строите, но не беспокойтесь слишком сильно. Если вам нужен высокооптимизированный IL, вам, вероятно, следует просто создать его самостоятельно. Но в большинстве случаев деревья LINQ/DLR работают нормально.
1 Если вы когда-нибудь задумывались, почему выражения LINQ/DLR так педантично требуют точного сопоставления типов, то это потому, что они предназначены для использования в качестве цели компилятора для нескольких языков, каждый из которых может иметь различные правила, касающиеся связывания методов, неявных и явных преобразований типов и т. д. Поэтому при построении деревьев LINQ/DLR вручную вы должны выполнять работу, которую компилятор обычно выполняет за кулисами, например автоматически вставлять код для неявных преобразований.
person
Mike Strobel
schedule
14.10.2013