【问题标题】:Is Multiplying the Inverse Better or Worse?乘以倒数更好还是更差?
【发布时间】:2009-03-17 18:35:32
【问题描述】:

当处理 double 数据类型时乘以逆是好还是坏?

哪种方式更快? 哪种方式使用更少的内存? 首选哪种方式?

MSIL 如何处理这个问题?

SquareInches = MMSquared / 645.16 
SquareInches = MMSquared * 0.0015500031000062000124000248000496

注意: 10K 用户会注意到这是this question 的副本, 因为原版被删了 提问者决定训斥 'cmets' 部分的每个人 问题。

这个问题被转发是因为它是一个“好”的问题。

请“取消选中”社区 Wiki 以获得您的答案,因为我只是将其发布为 CW,因此它不会被视为“声誉”抢夺。

相关问题:

Should I use multiplication or division?

【问题讨论】:

  • 这个听起来很熟悉,我想它可能是重复的。
  • 我真的希望我们可以通过用户语音请求来强制将人们的名字放在攻击性标签旁边,如果他们点击它。这又是一个滥用案例。
  • @JaredPar :公平地说,OP 斥责了所有要求他澄清问题的人,将其标记为冒犯是公平的,因为这是惩罚他虐待行为的唯一方法。
  • 那么 Python 和 .NET 的处理方式是否相同,David?

标签: .net algorithm optimization math


【解决方案1】:

乘以倒数更快。编译器不会自动对此进行优化,因为它可能会导致少量精度损失。 (这实际上出现在一个 D 新闻组 Walter Bright 经常出现的地方,他明确表示编译器不会自动执行此操作。)您通常应该划分,因为它更具可读性和准确性。

如果你在一个循环中执行一个浮点代码十亿次并且你不关心精度的小损失并且你会分裂多次乘以相同的数字,然后乘以倒数可能是一个很好的优化。实际上,在某些情况下,我在现实世界中获得了显着的加速,例如通过乘以倒数所描述的情况,但这些是循环执行数十亿次的极端极端情况,除了乘以浮点数之外几乎什么都不做。

【讨论】:

  • 一个简单的例子:光线追踪器需要对每条光线进行矢量归一化或类似的事情(通常每帧每像素几次)。
  • 如果您要进行乘法运算,因为您每秒要执行无数次,那么没有什么能阻止您添加带有除法值的注释以提高可读性。
【解决方案2】:

哪个是“faster”确实是特定于 cpu 的问题,或者至少是特定于 CPU 的问题,是的,除法通常被视为比乘法慢。当然,所有性能问题都可以用“视情况而定”来回答。

但是,如果您问哪个“更好”而不是哪个更快,那么会有一个明确的答案,可读性越高越好。您正在查看的性能改进可能在几个时钟周期的数量级上,因此除非您谈论这样做数百万次,否则您会尝试为自己节省几微秒。而且没有值得牺牲可读性和可维护性的微秒优化

【讨论】:

    【解决方案3】:

    收益将非常小或为零,具体取决于编译器和硬件。

    但它仍然很重要(在一个紧密的循环中),然后为了可读性你应该写

    SquareInches = MMSquared * (1 / 645.16)
    

    最好使用 645.16 的常量。

    【讨论】:

    • 如何才能比这两者中的任何一个更快?
    • 因为每个编译器都会执行 (1 / 645.16) 编译时间。所以它只是 0.0015...0496 的不同(和更好)符号
    • 哦,对了,没看到。这肯定是更好的表示法,+1。
    【解决方案4】:

    答案将取决于执行环境的架构。一般来说,在大多数处理器上,除法的成本通常比乘法略高。

    所以除非这实际上是性能问题,否则我可能不会担心。选择更易于理解的转换因子。

    【讨论】:

    • 编译器不会对此进行优化,因为它可能会稍微影响精度。
    【解决方案5】:

    来自我的 VB 代码计时器

    Dim SquareInches As Double
    Dim MMSquared As Double = 81
    Const d As Double = 645.16
    Const m As Double = 1 / 645.16
    Private Function TestCase1() As Boolean 'One
        'Test One Code Here
        SquareInches = MMSquared / d
        'end test code
        Return True
    End Function
    Private Function TestCase2() As Boolean 'Two
        'Test Two Code Here
        SquareInches = MMSquared * m
        'end test code
        Return True
    End Function
    

    结果

         3/17/2009 2:13:27 PM   CPU - 1.794GHz
     One - Using Division
     Two - Using Multiplication
     Outer Loops(OL)=7  Inner Loops=262,144
      ( times in ticks.  1 ms. = 10,000 ticks )
     >> Two faster, 0.0488 ticks/loop
      Ticks / Loop
     0.0342         0.0819          0.0331          0.0488
     OL Base        One             Two              One - Two
     1   8,936          21,459          8,609           12,850
     2   9,008          21,416          8,682           12,734
     3   8,965          21,423          8,643           12,780
     4   8,964          21,457          8,659           12,798
     5   8,966          21,469          8,640           12,829
     6   8,987          21,660          8,688           12,972
     7   8,963          21,429          8,802           12,627
    
      Average
     8,969          21,473          8,674           12,799
      Variance
     431.4          6,160.3         3,315.9       
      Standard Deviation
     20.8           78.5            57.6          
     3/17/2009 2:13:27 PM
    

    【讨论】:

    • 作为一个相对测量值,我想它是可以的。作为时间的绝对度量,这可能是另一回事。时间来自 Stopwatch 类。
    【解决方案6】:

    在大多数情况下,除法算法比乘法算法

    这是一种权衡,您可以选择更易读的方式或更快的方式。

    // Using division operation
    SquareInches = MMSquared / 645.16 
    

    这很容易阅读和维护,但比它的乘法对应物执行得慢:

    // Using a multiplication 
    SquareInches = MMSquared * 0.0015500031000062000124000248000496
    

    如果采用这种方式,您将需要更多的内存空间来存储倒数数字,但算法的运行速度要快得多。一位用户在 VS2005 项目上对其进行了测试,结果表明乘法版本的性能提高了 8 倍。

    原因是乘法可以盲目地转换为处理器上的移位和加法运算,这是 CPU 上最优化的运算。有符号乘法的一个很好的算法是 Booth 算法(处理器会为您执行此操作)。另一方面,在执行除法算法时需要更多的控制开销,从而使除法算法变慢。

    如果您需要性能,请使用加法、减法(无非是添加二进制补码)、乘法、移位,但不要使用除法。如果您提前计算所有逆数,并在除法密集型程序中使用它们进行乘法运算,您将获得实质性不可忽略的改进。

    【讨论】:

    • 如果两个数字都转换为双精度,那么它们使用的内存量相同。
    【解决方案7】:

    关于执行 mult 优化的编译器,他们可以对此进行优化(GCC 可以): SquareInches = MMSquared * (1 / 645.16)。

    【讨论】:

      【解决方案8】:

      如果您要除以 645.16 之类的文字值,那么很可能没有区别,因为编译器可以轻松确定哪个版本更快并使用它。
      如果您要除以或乘以一个变量,那么乘法可能会稍微快一些,因为逻辑通常更简单。
      与任何事情一样,可以肯定的是,使用分析器。

      【讨论】:

      • 至少 x86-32 上的 GCC -O2 不会执行此类优化并生成 fdivrs 指令。这样的优化充满了潜在的陷阱,所以我怀疑很多编译器会这样做。
      【解决方案9】:

      乘法和加法是处理器支持的最快运算。有些处理器甚至没有除法、平方根等的硬件实现。

      【讨论】:

        【解决方案10】:

        对于大多数处理器,乘法比除法更快。但对于大多数应用程序来说,它确实微不足道,在我看来,你最好选择更具可读性的那个,除非分析表明它是一条关键路径。

        如果它是一种解释性语言,那么读取源代码并将其转换为数字所花费的时间将超过实际进行数学计算所花费的时间,尤其是当您使用那么多有效数字进行乘法运算时。 (你确定你真的需要那么多有效数字吗?)

        【讨论】:

          【解决方案11】:

          我认为第一种方法显然是首选,因为它是明确的。想象一下在别人的代码中找到这个。你怎么确定 0.00155... 真的是 1/645.16?如果原来的程序员犯了错误怎么办?此外,我怎么知道 645.16 是正确的转换因子?为简单起见,最好不要将数字压缩或统一表示。最基本的例子如下:

          //hours per day * days per year
          int hoursPerYear = 24*365;
          

          我们可以清楚地看到这个数字是正确的,但是你怎么知道,8760 是正确的答案?如果您需要执行许多这些操作,您可能需要在输入密集计算之前将数据预处理为正确的形式。以这种方式,您将不需要令人难以置信的效率,而且问题变得没有实际意义。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-10
            • 2023-04-04
            • 1970-01-01
            • 2011-03-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多