C#编译器或Jitter会优化这些算术运算吗?

Joa*_*nge 2 .net c# compiler-construction optimization jit

假设我有这样的事情:

for (int i = 0; i < 1001; i++)
{
    double step = i / 1000.0;

    // do some math here
}
Run Code Online (Sandbox Code Playgroud)

基本上转向:

double step = i / 1000.0;
Run Code Online (Sandbox Code Playgroud)

进入这个:

double step = i * 0.001;
Run Code Online (Sandbox Code Playgroud)

我不确定是否可以在不改变程序结果的情况下进行这种更改,但是想知道C#编译器或抖动是否做了类似的事情?如果没有,为什么?我认为它不值得,或者它们还没有添加这个优化.

Eri*_*ert 8

让我们把它分解成几个问题:

抖动能否合法地d / 1000.0变成d * 0.001

不,因为这两个计算给出了不同的结果.请记住,浮点数是二进制分数,而不是小数分数; 当一个双精度等于1/3时,0.001作为双精度并不完全等于1/1000大于0.333333333.0.001是最接近1/1000的分数,可用52个二进制位表示.因此,存在x/1000.0不等于x*0.001的值.

抖动能否合法地d / 2.0变成d * 0.5

是.在这种情况下,值可以精确地以二进制表示,因为1/2在底部具有小的2的幂.

抖动也可以改变整数除法和乘法类似x / 2x * 2x >> 1x << 1.

抖动是否合法?

我不知道.试试吧!

您要做的是编译程序"零售",然后启动它而不是在调试器中运行它直到您知道有问题的代码已经被jitted.然后附加调试器并检查jitted代码.如果抖动知道附加了调试器,则抖动将生成更糟糕的代码,因为它试图生成更容易调试的代码.

我认为它不值得,或者它们还没有添加这个优化.

对于除法乘法,你假设乘法比除法快.现代芯片在这两方面都相当不错; 虽然除法通常需要更多位操作,但差异可能是可以忽略的.

  • @Joan只是检查`0.25`是否可以用二进制精确表示(显然它适合单/双IEEE-754格式)或不.在你的情况下:是的它可以:`.01` (4认同)

har*_*old 5

你可以尝试一下,但今天我感觉很慷慨,所以我为你做了.

测试1:

    static void Test1(int i)
    {
        double x = i / 1000.0;
        if (x == 0)
            throw new Exception();
    }
Run Code Online (Sandbox Code Playgroud)

(抛出是为了便于在恰当的时刻附加调试器)

反汇编(64位):

cvtsi2sd    xmm0,dword ptr [rsp+60h] 
divsd       xmm0,mmword ptr [000000C8h] 
Run Code Online (Sandbox Code Playgroud)

反汇编(32位):

fild        dword ptr [ebp-4] 
fdiv        dword ptr ds:[0460012Ch] 
Run Code Online (Sandbox Code Playgroud)

好的,测试代码2:i / 2.0
反汇编(64位):

cvtsi2sd    xmm0,dword ptr [rsp+60h] 
divsd       xmm0,mmword ptr [000000C8h] 
Run Code Online (Sandbox Code Playgroud)

反汇编(32位):

fild        dword ptr [ebp-4] 
fdiv        dword ptr ds:[0460012Ch] 
Run Code Online (Sandbox Code Playgroud)

结论:不,JIT编译器不进行此优化.
有关系吗?不经常.您可以通过书写i * (1 / 1000.0)或其他方式轻松"修复"它(在这种情况下必须进行常量折叠 - 不要删除括号).

JIT编译器不会做这个整数优化.