0

我有一些 C# 代码执行一些涉及原始数据类型的计算,例如:

public sealed class Calculation
{
    private readonly int a, b, c;

    public Calculation(int _a, int _b, int _c)
    {
        this.a = _a; this.b = _b; this.c = _c;
    }

    public int DoCalculation(int rfactor, int lfactor)
    {
        return (a / rfactor) + (b / lfactor) + ((a/b)*(rfactor+lfactor));
    }
}

如果 {a,b,c} 是编译时常量,则 DoCalculation(...) 方法中的表达式可以在 CIL 级别进行极大优化。我想知道 JITer 是否会优化 DoCalculation(...) 方法,类似于给出“只读”提示的编译时间常数优化。

4

2 回答 2

4

不,因为变量不是常量。它们的值在 jitting 时是未知的。只读并不会改变它们的运行时值未知并且实例之间可能不同的事实。

于 2012-07-13T21:09:26.887 回答
4

您的示例不是一个很好的示例,但是是的,这种优化肯定会发生。在不止一个级别上,C# 编译器首先获得了机会。它将用文字值评估简单的表达式并用结果替换它们。还有它可以在编译时检测溢出的方式。

抖动优化器也会这样做,通常是内联方法的结果。但这不会发生在您的 DoCalculation() 方法中,因为它太复杂而无法进行内联。但是一个微不足道的,比如:

    public static int DoCalculation(int a, int b) {
        return a * b;
    }

肯定会被内联和优化。像 Console.WriteLine(Calculation.DoCalculation(4, 5)) 生成:

00000003  call        5927D408 
00000008  mov         ecx,eax 
0000000a  mov         edx,14h                      // <=== here
0000000f  mov         eax,dword ptr [ecx] 
00000011  mov         eax,dword ptr [eax+38h] 
00000014  call        dword ptr [eax+14h] 

请注意结果 4 * 5 = 20 = 0x14 是如何预先计算的。

在优化器放弃之前,该方法何时可以被内联以及表达式的参与程度没有硬性规定。这可能会发生变化,并且取决于抖动的类型(x86 vs x64 vs ARM)。

如果此代码位于您的关键路径深处,并且探查器告诉您它负责 80+% 的执行时间(不要跳过探查器!)那么值得您花时间修改该方法并查看你得到什么。调试发布模式构建并确保允许优化器运行。工具 + 选项,调试,常规,取消勾选“在模块加载时抑制 JIT 优化”选项。

微小的变化可以产生很大的影响,当您通过看似微不足道的编辑将代码速度提高一倍时,感觉非常好。或者不是,经过一天的尝试,你一无所获,没有保证。但是你肯定会对什么样的代码运行良好产生一种“感觉”,一种非常有价值的经验和洞察力。

于 2012-07-13T21:20:04.563 回答