【发布时间】:2012-03-29 18:22:16
【问题描述】:
是否有一个属性可以告诉编译器必须始终优化方法,即使未设置全局 /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 之外没有任何内容。但是对于未优化的版本,我不太确定......递归调用的结果存储在局部变量中,然后有一个无用的分支,只是跳转到下一条指令,局部变量被加载并返回。所以我没有简单的方法来检查递归调用是否真的是最后一条指令,所以我不能确定是否可以应用尾调用优化。
【问题讨论】:
-
AFAIK,不 - 这是不可能的
-
JIT 编译器总是会优化每一个方法。
-
@Steven,如果你告诉它不要这样做(例如,
MethodImplAttribute中的NoOptimization标志)。但无论如何,我的问题是关于编译器优化,而不是 JIT 优化,因为我对生成的 IL 代码感兴趣。 -
我希望看到您记录您遇到问题的确切类型的 IL。在 C# 编译器中打开优化几乎没有影响 afaik,它只是省略了编译器发出的 NOP 操作码以使调试更容易。我能想到的唯一实用的解决方案是将代码移动到一个单独的程序集中,然后您可以在启用优化的情况下构建它。
-
@HansPassant,不仅仅是
nop操作码(我可以很容易地忽略这些),IL 代码的流程实际上是不同的。我更新了我的问题以添加更多详细信息。
标签: c# optimization compiler-construction il