【问题标题】:c++ practical computational complexity of <cmath> SQRT()c++ <cmath> SQRT() 的实际计算复杂度
【发布时间】:2011-10-16 14:17:19
【问题描述】:

两者之间的 CPU 周期(或者本质上是“速度”)有什么区别

 x /= y;

 #include <cmath>
 x = sqrt(y);

编辑:我知道操作并不相同,我只是随意提出x /= y 作为x = sqrt(y) 的基准

【问题讨论】:

  • 高度依赖于编译器、配置和目标CPU。
  • 虽然比较两个不同的操作可能听起来很奇怪,但这绝对是可能的(即使平台依赖并且很难做到正确)。在进行低级优化时,了解基本浮点运算的近似相对速度很重要。有时您可以通过乘以 4 并除以 3 或乘以 2 并执行平方根 2 来解决相同的问题,例如(人工示例)。
  • 伙计们,虽然不完全清楚,但我相信这是一个真实的问题。 @Matt:在没有专用硬件的不太强大的系统上, sqrt 通常比 div 慢 x10。在这十年的任何硬件上,它们都非常接近,并且经常通过流水线组合成类似的浮点性能。您可以搜索特定处理器上的 CPU 时序以获得更好的感觉。
  • 在这里friweb.hu/instlatx64 您可以找到所有 x86 指令的测量时序(ns 和滴答声)。例如。对于 Core 2 Duo E6700,x87 sqrt 操作的延迟 (L) 为 32 位浮点数的 29 个滴答; 64 位双精度数为 58 个刻度,80 位长双精度数为 69 个刻度; 32/64 位压缩浮点的 SSE/SSE2 时间相同(29 和 58 滴答声)。对于 F.P.分频:32bit=18clock; 64位=32时钟; 80bit=38 滴答; x87 和 SSE/SSE2 的 32/64 位相同。在您的操作中加载和存储一个值,必须另外计算。这应该是答案,但有些人关闭了这个很好的 Q。
  • @Mat 但是在某些情况下可以避免计算平方根。

标签: c++ complexity-theory sqrt cpu-cycles cpu-time


【解决方案1】:

您的问题的答案取决于您的目标平台。假设您使用的是最常见的 x86 cpu,我可以给您这个链接 http://instlatx64.atw.hu/ 这是测量指令延迟的集合(CPU 需要多长时间才能获得参数后得到结果)以及它们如何为许多 x86 流水线化和 x86_64 处理器。如果您的目标不是 x86,您可以尝试自己衡量成本或查阅您的 CPU 文档。

首先你应该得到一个你的操作的反汇编器(来自编译器,例如 gcc:gcc file.c -O3 -S -o file.asm 或通过编译二进制的反汇编,例如在调试器的帮助下)。 请记住,在您的操作中加载和存储一个值,必须另外计算。

这里有两个来自 friweb.hu 的例子:

对于 SQRT(x87、SSE 和 SSE2 版本)的 Core 2 Duo E6700 延迟 (L)

  • 32 位浮点数为 29 个刻度; 64 位双精度 58 个滴答; 80 位长双精度的 69 个刻度;

除法(浮点数):

  • 32 位为 18 个刻度; 64 位 32 个滴答; 80 位 38 个滴答声

对于较新的处理器,成本更低,并且对于 DIV 和 SQRT 几乎相同,例如对于 Sandy Bridge 英特尔 CPU:

浮点 SQRT 是

  • 32 位 14 个刻度; 64 位 21 个刻度; 80 位 24 个滴答声

浮点除法是

  • 32 位 14 个刻度; 64 位 22 个滴答; 80 位 24 个滴答声

对于 32 位,SQRT 甚至更快。

所以:对于较旧的 CPU,sqrt 本身比 fdiv 慢 30-50%;对于较新的 CPU,成本是相同的。 对于较新的 CPU,这两种操作的成本都低于旧 CPU; 对于更长的浮动格式,您需要更多时间;例如对于 64 位,您需要 2 倍于 32 位的时间;但是 80 位比 64 位便宜。

此外,较新的 CPU 具有与标量 (x87) 相同速度的向量运算(SSE、SSE2、AVX)。向量是 2-4 个相同类型的数据。如果您可以调整循环以使用相同的操作处理多个 FP 值,您将从 CPU 获得更高的性能。

【讨论】:

  • 我确定它是隐含的,但我假设 sqrt 利用了这些 CPU 优化?
  • C++ cmath 使用与 math.h 的 C 版本相同的 sqrt()。但在内部 sqrt() 可能比 FSQRT 更多的 asm 代码,例如错误处理。此外,有时 gcc 不会内联调用 sqrt() 来代替调用,因此函数调用的开销会在这里。您需要检查您的函数的反汇编程序并将其用于名称中带有“sqrt”的机器代码。也尝试选项-ffast-math
【解决方案2】:

如果平方根函数没有在特殊的硬件或软件中实现,大多数库函数将使用牛顿法计算它,该方法二次收敛。

牛顿法是一种迭代方法:您进行初始猜测,计算试验结果,然后将其用于下一次猜测。你重复,直到你认为你有一个“足够接近”的结果。碰巧你可以用平方根证明你需要多少次迭代。每次通过循环,您都会获得另外两位数的精度,因此大多数实现将在 8-9 个循环内收敛到双精度极限。

如果您仔细阅读this,您会发现迭代牛顿法是每次迭代执行两次减法、一次乘法和一次除法。

【讨论】:

  • 你能解释一下“二次收敛”吗?
  • @duffymo 那么 是使用牛顿法实现 SQRT,还是利用了其他人提到的 CPU 优化?
  • 这道题是数值方法。它属于这里。 @Matt,我不知道你的具体实现。您的 C++ 编译器可能会插入机器优化版本的指令。
  • 关键是“我认为”。衡量它 - 分析您的代码并确定。您可能会对结果感到惊讶。
  • @KerrekSB 二次收敛大致意味着每次迭代精度的位数加倍。例如,迭代 1 的误差为 0.1,迭代 2 的误差为 0.01,迭代 3 的误差为 0.001,迭代 4 的误差为 0.00001,迭代 5 的误差为 0.000000001。
【解决方案3】:

作为一般经验法则:浮点除法和平方根都被认为是慢速运算(与加法或乘法等快速运算相比)。与除法相比,可以预期平方根的速度大致相同或稍慢(即性能降低约 1 倍 - 2 倍)。例如。在Pentium Pro

除法和平方根的延迟分别为 18 到 36 和 29 到 69 个周期

要获得更准确的答案,您需要深入了解您平台的架构手册或执行基准测试。

注意:许多现代平台还提供平方根逆运算,其速度与 sqrt 大致相同,但通常更有用(例如,通过使用 invsqrt,您可以同时计算 sqrt 和 div,每个乘法一次)。

【讨论】:

  • 对于来自英特尔的沙桥,这两项操作都需要完全相同的时间。所以,现在,sqrt 并不比 div 慢 2 倍
  • 好的。调整。可以包括许多平台的确切时间,但我认为这个问题只想有一种“直觉”,在极少数情况下,您确实需要准确的数据,更重要的是知道在哪里或如何获得它们。
  • 两个确切的例子给了我一些感觉。
猜你喜欢
  • 1970-01-01
  • 2017-11-04
  • 2012-12-31
  • 2019-03-11
  • 1970-01-01
  • 2013-11-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-08
相关资源
最近更新 更多