假设我有这样的东西:
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# 编译器或抖动是否会执行类似的操作?如果没有,为什么?我认为要么不值得,要么他们还没有添加此优化。
让我们把它分成几个问题:
抖动可以合法的把
变成d / 1000.0
吗?d * 0.001
不,因为这两个计算给出不同的结果。请记住,浮点数是二进制分数,而不是十进制分数; 0.001 作为双精度数并不完全等于 1 / 1000,而 0.333333333 作为双精度数则不完全等于 1 / 3。0.001 是可以用 52 个二进制位表示的最接近 1/1000 的分数。因此,存在 x / 1000.0 不等于 x * 0.001 的值。
抖动可以合法的把
变成d / 2.0
吗?d * 0.5
是的。在这种情况下,这些值可以精确地用二进制表示,因为 1/2 的底部有 2 的小幂。
抖动还可以将整数除法和乘法(如
x / 2
或 x * 2
)更改为 x >> 1
或 x << 1
。
在合法的情况下抖动真的会这样做吗?
我不知道。试试吧!
您需要做的是编译“零售”程序,然后启动它而不是在调试器中并运行它,直到您知道有问题的代码已被编译。然后连接调试器并检查抖动代码。如果抖动知道附加了调试器,它会生成更糟糕的代码,因为它试图生成更容易调试的代码。
我认为要么不值得,要么他们还没有添加此优化。
对于除法到乘法的情况,您假设乘法比除法更快。现代芯片在这两方面都非常出色。尽管除法通常确实需要更多的位运算,但差异可能可以忽略不计。
你可以尝试一下,但我今天感觉很慷慨,所以我为你做了。
测试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
cvtsi2sd xmm0,dword ptr [rsp+60h]
divsd xmm0,mmword ptr [000000C8h]
反汇编(32位):
fild dword ptr [ebp-4]
fdiv dword ptr ds:[0460012Ch]
结论:不,JIT编译器没有做这个优化。
有关系吗?不经常。您可以通过编写
i * (1 / 1000.0)
或类似的内容轻松“修复”它(在这种情况下,必须强制折叠 - 不要删除括号)。
JIT 编译器会对整数进行这种优化。
我从这两种方法开始:
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
如您所见,它不会将乘法更改为除法或将除法更改为乘法。 不过,我不清楚一个操作如何相对于另一个操作进行优化。
编辑:忘记了抖动。 嗯,这取决于平台。 所以我想,除非你是埃里克·利珀特,否则根本不可能回答。
这就是你要问的吗? 我有点不确定..
for(double i = .001; i < 1.001; i+=.001){
//TODO: Implement
}