假设我有这样的事情:
for (int i = 0; i < 1001; i++)
{
double step = i / 1000.0;
// do some math here
}
基本上转向:
double step = i / 1000.0;
进入这个:
double step = i * 0.001;
我不确定是否可以在不改变程序结果的情况下进行这种更改,但是想知道C#编译器或抖动是否做了类似的事情?如果没有,为什么?我认为它不值得,或者它们还没有添加这个优化。
答案 0 :(得分: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 / 2
或x * 2
)更改为x >> 1
或x << 1
。
合法时抖动是否真的这样做了?
我不知道。试试吧!
你要做的是编译程序“零售”,然后启动它而不是在调试器中并运行它,直到你知道有问题的代码被jitted。然后附加调试器并检查jitted代码。如果抖动知道附加了调试器,那么抖动将产生更糟糕的代码,因为它试图生成更容易调试的代码。
我认为它不值得,或者它们还没有添加此优化。
对于除乘乘法,你假设乘法比除法快。现代芯片在这两方面都相当不错;虽然除法通常需要更多位操作,但差异可能是可以忽略不计的。
答案 1 :(得分:5)
你可以尝试一下,但我今天感觉很慷慨,所以我为你做了。
测试1:
static void Test1(int i)
{
double x = i / 1000.0;
if (x == 0)
throw new Exception();
}
(抛出是为了便于在恰当的时刻安装调试器)
反汇编(64位):
cvtsi2sd xmm0,dword ptr [rsp+60h]
divsd xmm0,mmword ptr [000000C8h]
反汇编(32位):
fild dword ptr [ebp-4]
fdiv dword ptr ds:[0460012Ch]
好的,测试代码2:i / 2.0
反汇编(64位):
cvtsi2sd xmm0,dword ptr [rsp+60h]
divsd xmm0,mmword ptr [000000C8h]
反汇编(32位):
fild dword ptr [ebp-4]
fdiv dword ptr ds:[0460012Ch]
结论:不,JIT编译器不进行此优化
有关系吗?不经常。你可以轻松地修复&#34;通过编写i * (1 / 1000.0)
或其他一些内容(在这种情况下必须进行常量折叠 - 不要删除括号)。
JIT编译器 对整数执行此优化。
答案 2 :(得分:4)
我从这两种方法开始:
public static double Division(double i)
{
return i / 1000.0;
}
public static double Multiplication(double i)
{
return i * 0.001;
}
编译,然后在ILSpy中打开程序集。这是由此产生的IL:
.method public hidebysig static
float64 Division (
float64 i
) cil managed
{
// Method begins at RVA 0x2052
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.r8 1000
IL_000a: div
IL_000b: ret
} // end of method Program::Division
.method public hidebysig static
float64 Multiplication (
float64 i
) cil managed
{
// Method begins at RVA 0x205f
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.r8 0.001
IL_000a: mul
IL_000b: ret
} // end of method Program::Multiplication
正如您所看到的,它不会将乘法转换为除法或将除法转换为乘法。我不清楚一个操作如何优化另一个操作。
编辑:忘记抖动。嗯,那是平台依赖的。我认为,除非你是Eric Lippert,否则它甚至不可能回答。
答案 3 :(得分:0)
这就是你问的问题吗?我有点不确定..
for(double i = .001; i < 1.001; i+=.001){
//TODO: Implement
}