2012-03-13 61 views
15

是否有一个属性可用于告诉编译器必须始终优化一个方法,即使全局/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之外的通话之后没有任何内容。但是对于未优化的版本,我不太确定......递归调用的结果存储在本地变量中,然后有一个无用的分支跳转到下一条指令,本地变量被加载并返回。所以我没有简单的方法来检查递归调用是否是最后的指令,所以我不能确定是否可以应用尾部调用优化。

+1

AFAIK,no - 这是不可能的 – 2012-03-13 10:56:01

+1

JIT编译器将始终优化每个方法。 – Steven 2012-03-13 11:01:39

+0

@Steven,如果你不告诉它(例如使用'MethodImplAttribute'中的'NoOptimization'标志)。但无论如何,我的问题是关于编译器优化,而不是JIT优化,因为我对生成的IL代码感兴趣。 – 2012-03-13 11:17:34

回答

2

如果您将用作动态方法模板的方法相对简单 - 并且不依赖于其他方法。然后把它放在它自己的程序集中,并为那个程序集打开优化。

就原始问题而言,因为MSIL是一种基于堆栈的语言。而规格保证堆栈状态在ret声明,你可以100%确定你可以添加一个尾部前缀没有问题。但是,实际上也不太可能增加任何好处,因为我没有真正看到JIT使用尾部前缀来实际优化最终的jitter代码。

+0

Downvoter谨慎指出哪些不正确? – 2012-03-15 18:45:45

+0

相关:http://stackoverflow.com/questions/491376 - 显然'.tail'在x64中进行了优化,但不是x86 – 2013-04-30 21:25:45

0

是否有无论如何你可以使用Microsoft.CSharp.CSharpCodeProvider动态生成原始方法代码?

如果您控制方法编译,您可以在使用CompilerOptions调用编译器时设置选项。

+0

这不会帮助;我不想动态生成代码(不是我原意的代码) – 2012-03-14 23:51:01

0

只要您使用C#,您永远无法确定是否获得了尾部呼叫优化。

尤其是即使有call ... ret JITter也不保证尾部呼叫。因此,依靠尾部呼叫优化(避免堆栈溢出)的IMO C#代码简单地被破坏了。在C#尾部调用优化纯粹是一种性能优化。

使用可靠地发出尾部呼叫的语言,或重写您的方法,使其不需要尾部呼叫。

+0

我知道C#不适合用于尾部调用,我只是在做一个概念验证......不要担心,我不会打算在生产代码中使用这个) – 2012-03-14 23:52:37