C# 我可以强制编译器优化特定的方法吗?
是否有一个属性可以告诉编译器,即使未设置全局C# 我可以强制编译器优化特定的方法吗?,c#,optimization,compiler-construction,il,C#,Optimization,Compiler Construction,Il,是否有一个属性可以告诉编译器,即使未设置全局/o+编译器开关,也必须始终优化方法 我问这个问题的原因是因为我在玩弄基于现有方法的IL代码动态创建方法的想法;当代码经过优化时,我想做的操作相当容易,但在未优化的代码中,由于编译器生成的额外指令,操作会变得非常困难 编辑:关于困扰我的非优化的更多细节 让我们考虑下列阶乘函数的实现: static long FactorialRec(int n, long acc) { if (n == 0) return acc;
/o+
编译器开关,也必须始终优化方法
我问这个问题的原因是因为我在玩弄基于现有方法的IL代码动态创建方法的想法;当代码经过优化时,我想做的操作相当容易,但在未优化的代码中,由于编译器生成的额外指令,操作会变得非常困难
编辑:关于困扰我的非优化的更多细节
让我们考虑下列阶乘函数的实现:
static long FactorialRec(int n, long acc)
{
if (n == 0)
return acc;
return FactorialRec(n - 1, acc * n);
}
(注意:我知道有更好的方法来计算阶乘,这只是一个例子)
启用优化后生成的IL非常简单:
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0005
IL_0003: ldarg.1
IL_0004: ret
IL_0005: ldarg.0
IL_0006: ldc.i4.1
IL_0007: sub
IL_0008: ldarg.1
IL_0009: ldarg.0
IL_000A: conv.i8
IL_000B: mul
IL_000C: call UserQuery.FactorialRec
IL_0011: ret
但未优化的版本则截然不同
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000A: brtrue.s IL_0010
IL_000C: ldarg.1
IL_000D: stloc.0
IL_000E: br.s IL_001F
IL_0010: ldarg.0
IL_0011: ldc.i4.1
IL_0012: sub
IL_0013: ldarg.1
IL_0014: ldarg.0
IL_0015: conv.i8
IL_0016: mul
IL_0017: call UserQuery.FactorialRec
IL_001C: stloc.0
IL_001D: br.s IL_001F
IL_001F: ldloc.0
IL_0020: ret
它设计为在末端只有一个出口点。要返回的值存储在局部变量中
为什么这是一个问题?我想动态生成一个包含尾部调用优化的方法。通过在递归调用之前添加
tail.
前缀,可以很容易地修改优化方法,因为调用之后除了ret
之外没有其他内容。但对于未优化的版本,我不太确定。。。递归调用的结果存储在一个局部变量中,然后有一个无用的分支跳转到下一条指令,加载并返回局部变量。因此,我没有简单的方法来检查递归调用是否真的是最后一条指令,因此我无法确定是否可以应用尾部调用优化。是否可以使用Microsoft.CSharp.CSharpCodeProvider动态生成原始方法代码
如果控制方法编译,则可以在使用调用编译器时设置选项。只要使用C#,就永远无法确保获得尾部调用优化 特别是即使使用
调用。。。ret
抖动不保证尾部调用。因此,依靠尾部调用优化(以避免堆栈溢出)的IMO C#代码被彻底破坏了。在C#中,尾部调用优化纯粹是一种性能优化
使用可靠发出尾部调用的语言,或者重写方法,使其不需要尾部调用。如果将用作动态方法模板的方法相对简单,并且不依赖于其他方法,则使用该语言。然后把它放在它自己的组件中,并为该组件启用优化
至于最初的问题,因为MSIL是一种基于堆栈的语言。在
ret
语句中,specs保证堆栈状态,您可以100%确保可以添加尾部前缀而不会出现问题。然而,这也不太可能真正增加任何好处,因为我还没有真正看到JIT使用尾部前缀来实际优化最终的JIT代码。AFAIK,no-这是不可能的JIT编译器将始终优化每个方法。@Steven,如果你告诉它不要这样做,就不会(例如,使用MethodImpl属性中的NoOptimization
标志
).但无论如何,我的问题是关于编译器优化,而不是JIT优化,因为我对生成的IL代码感兴趣。我希望看到您记录您遇到问题的IL的确切类型。在C#编译器中启用优化几乎没有效果,它只是忽略了编译器向其发出的NOP操作码使调试更容易。我能想到的唯一实用的解决方案是将代码移动到一个单独的程序集中,然后您可以在启用优化的情况下简单地构建该程序集。@HansPassant,这不仅仅是nop
操作码(我可以很容易地忽略它们),IL代码的流程实际上是不同的。我更新了我的问题以添加更多细节。这不会有帮助;我不想动态生成代码(不是我的意思是原始代码)。我知道C#不适合尾部调用,我只是在做概念验证……别担心,我不打算在生产代码中使用它;)相关:-显然.tail
在x64中进行了优化,但在x86中没有优化