C#/XNA-乘法比除法快?

C#/XNA-乘法比除法快?,c#,optimization,xna,compiler-optimization,C#,Optimization,Xna,Compiler Optimization,我最近看到了一条让我困惑的推特(这是一位XNA程序员在编写XNA游戏时发布的): 我很惊讶,因为我一直认为编译器非常智能(例如,使用位移位),最近读了一篇文章。我想知道这里面有多少真实性,因为在我的游戏中有很多计算 我询问,希望得到一份样品,但是原始海报无法提供。然而,他确实说过: 所以,我很好奇 有人能举一个例子,说明一些代码可以将除法改为乘法并获得性能提升,而C#编译器本身却无法做到这一点。要给出一些数字,请参阅本pdf 对于奔腾,我们得到了一些数字,但它们并不好: IMUL 10或

我最近看到了一条让我困惑的推特(这是一位XNA程序员在编写XNA游戏时发布的):

我很惊讶,因为我一直认为编译器非常智能(例如,使用位移位),最近读了一篇文章。我想知道这里面有多少真实性,因为在我的游戏中有很多计算

我询问,希望得到一份样品,但是原始海报无法提供。然而,他确实说过:

所以,我很好奇


有人能举一个例子,说明一些代码可以将除法改为乘法并获得性能提升,而C#编译器本身却无法做到这一点。

要给出一些数字,请参阅本pdf

对于奔腾,我们得到了一些数字,但它们并不好:

  • IMUL 10或11
  • FMUL 3+1
  • IDIV 46(32位操作数)
  • FDIV 39

我们谈论的是巨大的差异

如果您给大多数编译器一个机会,它们可以进行合理的优化工作。例如,如果你除以一个常数,编译器很有可能会优化它,这样它就可以像任何你可以合理地替换它的东西一样快速地完成

然而,当你有两个事先不知道的值,你需要将一个值除以另一个值来得到答案,如果编译器有很多方法来处理它,它就会-,对于这个问题,如果编译器有很大的空间来优化它,CPU就会这样做,所以编译器不必这样做

编辑:对于类似的事情(这相当现实),您的最佳选择可能是:

double scale_factor = get_input();

for (i=0; i<values.size(); i++)
    values[i] /= scale_factor;
scale_factor = 1.0 / scale_factor;

for (i=0; i<values.size(); i++)
    values[i] *= scale_factor;
double scale_factor=get_input();

对于(i=0;i虽然编译器可以通过2的幂优化除法和乘法,但其他数字可能很难或不可能优化。尝试将除法优化为17,您就会明白原因。当然,这是假设编译器不知道您提前将除法为17(这是一个运行时变量,而不是常量).

有点晚了,不过没关系

你的问题的答案是肯定的

请看我在这里的文章,它使用了基于对您的问题的第一条评论中提到的文章的信息

我有一个QuickDivideInfo结构,它存储给定除数的幻数和移位,从而允许使用更快的乘法计算除法和模QuickDivideInfo用于黄金素数列表。至少对于x64,QuickDivideInfo上的.Divide方法是内联的,比使用Divide运算符(在i5上)快3倍;它适用于除int.MinValue之外的所有分子,并且不能溢出,因为乘法在移位之前存储在64位。(我没有在x86上尝试过,但是如果由于某些原因它没有内联,那么Divide方法的整洁性将丢失,您将不得不手动内联它)

因此,如果您可以预先计算,上述方法将适用于所有场景(int.MinValue除外)。如果您信任生成幻数/移位的代码,那么您可以在运行时处理任何除数

其他著名的小除数,其分子范围非常有限,可以内联编写,如果它们不需要中间长除数,则速度可能更快


除以二的倍数:我希望编译器能够处理这个问题(如宽度/2),因为它是常量。如果没有,那么在(我不是问CPU上实际操作速度的差异,而是问写C#的差异,以及为什么编译器不能优化它。@DanTup你的意思是它应该乘以x 0.5而不是除以2?或(甚至更好)如果它是整数,它应该移位1?@xanatos我在问,在什么情况下编译器不能优化我可以轻松优化的东西。我应该将所有的“/2”更改为“*0.5”吗如果我有VARA*VARB,我应该把它改为除法吗?@ DanTup。如果你考虑了副作用,那么它可能不能。例如,用浮点,我不确定,5和*0.2是相同的。0.2不能用浮点表示(它在基2中是周期性的),所以你把错误移到“在你的操作之前”,而不是。“操作后”。1000000000/5与1000000000*0.2(其中0.2实际上是1.999999999999999…)相同吗?值得指出的是,您链接的资源是从1993年开始的。这些天的差异甚至没有那么大“正如你的回答所暗示的那样。@DanTup-Danny Tuppeny:重新安排整个算法以使其反向工作,这甚至超出了最复杂的编译器的能力。@Jerry这是有道理的,尽管我不认为推特的目的是这样做(我认为优化算法比“将除法改为乘法”更有效).我更新了我的问题,使之对我所追求的内容不那么含糊:有人能举一个例子,说明一些代码可以将除法改为乘法并获得性能提升,而C#编译器本身却不能做同样的事情。@Jerry:这种转换会产生同样的结果吗?我有一种浮点算术的感觉梅蒂奇会在某个地方把它搞砸……当然,在大多数情况下,微小的差异可能并不重要,但我认为编译器这样做是不合适的。@Martinho Fernandes:我最近还没有读过C#spec的这一部分,所以不能肯定地说。我特别建议它,因为我并不真的希望大多数编译器都这样做,但是在许多情况下(例如,图形),结果会很好,转换也很容易。@Jerry该示例看起来相当简单,但我可以看出编译器不会/无法更改它的一些原因。我还没有测试出来(没有弄清楚如何在我的iPad上运行cs…)
 while(start<=end)
    {
    int mid=(start+end)/2;
    if(mid*mid==A)
    return mid;
    if(mid*mid<A)
    {
    start=mid+1;
    ans=mid;
    }
while(start<=end)
    {
    int mid=(start+end)/2;
    if(mid==A/mid)
    return mid;
    if(mid<A/mid)
    {
    start=mid+1;
    ans=mid;
    }
    else
    end=mid-1;
    }