【问题标题】:Is multiplication faster than float division? [duplicate]乘法比浮点除法快吗? [复制]
【发布时间】:2013-07-26 20:02:12
【问题描述】:

在 C/C++ 中,您可以设置以下代码:

double a, b, c;
...
c = (a + b) / 2;

这与以下内容完全相同:

c = (a + b) * 0.5;

我想知道哪个更好用。从根本上说,一种操作是否比另一种更快?

【问题讨论】:

  • 一般来说他们不会做同样的事情。
  • @GrijeshChauhan 快告诉你? 那个是关于什么的?
  • @JanSpurny:这不是高度依赖 CPU 或架构。它部分依赖于 CPU 或体系结构。在许多处理器上,乘法比除法快得多,如果没有其他原因,程序员应该更喜欢乘法而不是除法。
  • 反对票和关闭票是不合适的。这是一个很好的问题,会对性能产生重大影响。高性能代码通常更喜欢乘法而不是除法,因为在大多数现代处理器上乘法速度更快。
  • 这是一个重要问题(所以投赞成票)的另一个原因是 优化器无法进行这种转换,除非因子是 2 的幂(或者,如果使用的是非二进制浮点,则为浮点系统中的倒数)。当因子在浮点系统中没有精确的逆时,不允许优化器将除法更改为乘法。因此,程序员应该意识到乘法比除法更快,并且如果他们知道逆中的舍入误差是可以接受的,他们应该支持乘法。

标签: c++ c performance optimization


【解决方案1】:

乘法比除法快。在大学里,我被告知除法是乘法的六倍。实际的时序取决于架构,但一般来说,乘法永远不会像除法一样慢,甚至不会像除法一样慢。如果舍入误差允许,请始终优化代码以使用乘法。

所以在一个例子中,这通常会更慢......

for (int i=0; i<arraySize; i++) {
    a[i] = b[i] / x;
}

...比这个...

y=1/x;
for (int i=0; i<arraySize; i++) {
    a[i] = b[i] * y;
}

当然,如果存在舍入误差,使用第二种方法会降低(一点)精度,但除非您反复计算 x=1/x;,否则这不太可能造成太大问题。

编辑:

仅供参考。我在谷歌上搜索了第三方的操作时间比较。

http://gmplib.org/~tege/x86-timing.pdf

查看 MUL 和 DIV 上的数字。这表示 5 到 10 倍之间的差异取决于处理器。

【讨论】:

  • "无法理解为什么这被否决了这么多" 因为在 SO 上,任何问题甚至可能会立即接近未定义/未指定/实现相关的行为被否决,关闭,并在地毯下扫过。显然,迂腐比现实和务实的发展需要更重要。有点半开玩笑,但有时感觉就是这样。
  • @Thomas 是的。令人毛骨悚然的是,这甚至不是模棱两可或未定义的。处理器制造商通常会发布统计数据。这是编译器执行优化的统计数据的来源。所以这里“未定义”的意思是“我还没读过”。啊啊啊啊啊好的。
  • @Thomas:我猜反对票来自这样一个事实,即这个问题非常琐碎,并且在右侧的“相关”列中自动显示了一个完全相同的副本(读作:没有研究努力),优化很可能过早(考虑乱序执行和内存带宽),并且代码 sn-p 包含隐式的 int-double 转换和与问题无关的额外操作。 (这只是一个猜测,当然,我不是投票者之一,所以不能说)
  • @Thomas:我倾向于对新手轻松。我们都曾或至少在某一时刻犯过错误,因此被烧焦并不会让任何人感到受欢迎。帮助经验不足的人是一件好事,而 StackOverflow 在这方面非常擅长。
  • 对于 x86,请参阅 agner.org/optimize。 div 和 mul 具有不同的吞吐量与延迟比率。两者都只有 1 uop,因此可以与其他操作很好地重叠。对于吞吐量而言,您的约 6 倍的成本看起来差不多,但如今 FP div 的延迟比更像是 4(在 Intel Haswell 上)。英特尔 Skylake 有一个非常流水线的 FP div 单元(每 4 个周期一个 double 结果的吞吐量)。 (256b SIMD 向量的吞吐量较低)。
【解决方案2】:

在这种情况下,编译器很可能会将除法转换为乘法,如果它“认为”它更快的话。浮点除以 2 也可能比其他浮点除法更快。如果编译器不转换它,使用乘法可能会更快,但不确定 - 取决于处理器本身。

在编译器无法确定这样做是否“安全”的情况下,手动使用乘法而不是除法的好处可能非常大(例如,0.1 不能完全存储为浮点数中的 0.1,它变成 0.10000000149011612)。有关可作为该类别代表的 AMD 处理器的数据,请参见下文。

要判断你的编译器是否能很好地做到这一点,你为什么不写一些代码来做实验。确保编写它以便编译器不只是计算一个常量值并丢弃循环中的所有计算。

编辑:

AMD 针对 Family 15h 处理器的优化指南,提供 fdivfmul 的数字分别为 42 和 6。 SSE 版本更接近一些,DIVPS、DIVPD DIVSS 和 DIVSD(除法)为 24(单)或 27(双)周期,所有形式的乘法为 6 个周期。

根据记忆,英特尔的数据并没有那么遥远。

【讨论】:

  • 我现在做错了什么,因为提供更多信息而被否决了?请告诉我,以便我学习!
  • 我没有投反对票,不知道别人为什么投反对票,但我希望你不介意我更正了你关于 0.1 的声明。
  • 您在第二段中的意思不是很清楚(括号永远不会关闭,并且句子有点拖尾。但是正如 cmets 对其他答案所指出的那样,编译器通常处理浮点代码时不会将除法转换为乘法。
  • @Jalf:是的,我已经稍微澄清了第二段。
【解决方案3】:

浮点乘法通常比浮点除法花费更少的周期。但是对于文字操作数,优化器很清楚这种微优化。

【讨论】:

  • 这是一种我们不能依赖优化器的情况,如果我们询问问题中的单个示例以外的情况。除以 2 的幂和乘以倒数在二进制浮点中是等价的,但除以其他因数的除法没有等价的乘法,因为倒数不能精确表示。这禁止优化器进行转换。因此,程序员应该意识到这一点,并且如果他们知道逆中的舍入误差是可以接受的,那么他们应该支持乘法。
  • @EricPostpischil:在我们不太关心精度的情况下,为“使用快速数学”提供标志是否“有帮助”?比如说,在 3d 游戏的情况下,您会认为浮点精度的最后几位无关紧要。
  • x/y —&gt; x * (1/y)-ffast-math 授权。具体来说,这是由-freciprocal-math 授权的,这是-ffast-math 中包含的“优化”之一。
猜你喜欢
  • 2011-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 2012-02-10
  • 2011-06-30
  • 2016-01-14
相关资源
最近更新 更多