naw*_*fal 7 c# il jit expression-trees compiler-optimization
好吧,这只是好奇心,没有现实世界的帮助.
我知道使用表达式树,您可以像常规C#编译器一样动态生成MSIL.由于编译器可以决定优化,我很想知道IL生成期间是什么情况Expression.Compile().基本上有两个问题:
因为在编译时编译器可以在调试模式和释放模式下产生不同的(可能是略微的)IL,在调试模式和发布模式下构建时通过编译表达式生成的IL是否存在差异?
此外,在运行时将IL转换为本机代码的JIT在调试模式和发布模式下都应该有很大的不同.编译表达式也是如此吗?或者来自表达树的IL根本没有被咬过?
我的理解可能有缺陷,请纠正我以防万一.
注意:我正在考虑分离调试器的情况.我问的是Visual Studio中"debug"和"release"附带的默认配置设置.
Mik*_*bel 12
因为在编译时编译器可以在调试模式和释放模式下产生不同的(可能是略微的)IL,在调试模式和发布模式下构建时通过编译表达式生成的IL是否存在差异?
这个实际上有一个非常简单的答案:没有.给定两个相同的LINQ/DLR表达式树,如果由在Release模式下运行的应用程序编译而另一个在Debug模式下编译,则生成的IL将没有区别.我不知道如何实现这一点; 我不知道任何可靠的代码方式,System.Core以了解您的项目正在运行调试版本或发布版本.
然而,这个答案实际上可能会产生误导.表达式编译器发出的IL在调试和发布版本之间可能没有区别,但是在C#编译器发出表达式树的情况下,表达式树本身的结构可能在调试和释放模式之间不同.我对LINQ/DLR内部结构非常熟悉,但与C#编译器相比并不多,所以我只能说这些情况可能有所不同(可能没有).
此外,在运行时将IL转换为本机代码的JIT在调试模式和发布模式下都应该有很大的不同.编译表达式也是如此吗?或者来自表达树的IL根本没有被咬过?
对于预优化的IL与未优化的IL ,JIT编译器吐出的机器代码不一定会有很大的不同.结果可能完全相同,特别是如果唯一的差异是一些额外的临时值.我怀疑两者在更大和更复杂的方法中会有更多分歧,因为JIT通常会花费时间/精力来优化给定方法.但听起来您对编译的LINQ/DLR表达式树的质量与调试或发布模式下编译的C#代码的比较感兴趣.
我可以告诉你,LINQ/DLR LambdaCompiler执行的优化很少 - 肯定比Release模式下的C#编译器少; 调试模式可能更接近,但我会把我的钱放在C#编译器上稍稍激进.在LambdaCompiler一般不会尝试减少使用临时当地人,像条件句,比较,类型转换操作通常会使用更多的中间当地人比你想象的.其实我可以只想到的是三份优化确实执行:
嵌套的lambda将尽可能内联(并且"在可能的情况下"倾向于"大部分时间").实际上,这可以帮助很多.请注意,这只能在你Invoke一个LambdaExpression; 如果在表达式中调用已编译的委托,则不适用.
至少在某些情况下,省略了不必要/冗余类型的转换.
如果在编译时已知a TypeBinaryExpression(即[value] is [Type])的值,则该值可以作为常量内联.
除了#3之外,表达式编译器不进行"基于表达式"的优化; 也就是说,它不会分析表达树寻找优化机会.列表中的其他优化很少或没有关于树中其他表达式的上下文.
通常,您应该假设编译的LINQ/DLR表达式产生的IL优于C#编译器产生的IL.但是,生成的IL代码有资格进行JIT优化,因此除非您实际尝试使用等效代码进行测量,否则很难评估实际性能影响.
在使用表达式树编写代码时要记住的一件事是,实际上,您是编译器1.LINQ/DLR树被设计为由一些其他编译器基础结构发出,如各种DLR语言实现.因此,您可以在表达级别处理优化.如果你是一个草率的编译器并发出一堆不必要的或冗余的代码,生成的IL将更大,并且不太可能被JIT编译器积极地优化.所以要注意你构建的表达式,但不要担心太多.如果你需要高度优化的IL,你应该自己发射它.但在大多数情况下,LINQ/DLR树表现得很好.
1如果您曾经想知道为什么LINQ/DLR表达式对于要求精确类型匹配如此迂腐,那是因为它们旨在用作多种语言的编译器目标,每种语言可能有关于方法绑定,隐式和显式类型的不同规则因此,在手动构建LINQ/DLR树时,必须完成编译器通常在幕后执行的工作,例如自动为隐式转换插入代码.