【问题标题】:Very slow std::pow() for bases very close to 1对于非常接近 1 的基数,std::pow() 非常慢
【发布时间】:2013-01-19 04:58:19
【问题描述】:

我有一个解方程 f(x) = 0 的数字代码,其中我必须将 x 提高到幂 p。我用一堆东西来解决它,但最后我有牛顿的方法。解决方案恰好等于x = 1,因此是我的问题的原因。当迭代的解决方案接近1,比如x = 1 + 1e-13,计算std::pow(x, p) 所需的时间会大大增加,很容易增加100 倍,使我的代码无法使用。

运行这个东西的机器是CentOS上的AMD64(Opteron 6172),命令很简单y = std::pow(x, p);。类似的行为出现在我所有的机器上,都是 x64。正如here 记录的那样,这不仅是我的问题(即,其他人也很生气),仅出现在 x64 上,并且仅出现在接近 1.0x 上。 exp 也发生了类似的事情。

解决这个问题对我来说至关重要。有谁知道是否有办法解决这种缓慢的问题?

编辑:John 指出这是由于非规范化造成的。那么问题来了,如何解决这个问题?代码是 C++,用g++ 编译,用于GNU Octave。看来,虽然我已将CXXFLAGS 设置为包括-mtune=native-ffast-math,但这并没有帮助,代码运行速度也一样慢。

目前的伪解决方案:对于所有关心这个问题的人来说,下面建议的解决方案对我个人来说并不适用。我真的需要std::pow() 的通常速度,但没有x = 1 周围的迟缓。我个人的解决方案是使用以下技巧:

inline double mpow(double x, double p) __attribute__ ((const));

inline double mpow(double x, double p)
{
    double y(x - 1.0);
    return (std::abs(y) > 1e-4) ? (std::pow(x, p)) : (1.0 + p * y * (1.0 + (p - 1.0) * y * (0.5 + (1.0 / 6.0) * (p - 2.0) * y)));
}

界限可以改变,但是对于 -40

【问题讨论】:

  • 这可能与subnormal numbers 的一般性能问题有关。浮点值非常接近 0 的计算可能比正常情况慢 100 倍。见stackoverflow.com/questions/9314534/…
  • 好点。关于如何解决这个问题的任何建议?如果数字足够接近,则将数字固定为 1?
  • @JohnKugelman:如果您阅读链接,这是因为 glibc 在给定某些输入值时使用了一个慢得多的函数(名为 __slowpow)。
  • @fledglingCxxuser -ffast-math 违反了 IEEE754 合规性。这是真的坏还是好的取决于你的用例,但如果我是你,我会在启用该标志之前做一些进一步的研究以了解它在做什么。

标签: c++ performance glibc pow


【解决方案1】:

明显的解决方法是注意在实数中,a ** b == exp(log(a) * b) 并改用该形式。您需要检查它是否不会对结果的准确性产生不利影响。编辑:正如所讨论的,这也受到了几乎同样程度的放缓的影响。

问题不在于异常,至少不是直接的;尝试计算 exp(-2.4980018054066093e-15) 会遇到同样的减速,-2.4980018054066093e-15 肯定不是异常的。

如果您不关心结果的准确性,那么缩放指数或指数应该会让您超出慢速区域:

sqrt(pow(a, b * 2))
pow(a * 2, b) / pow(2, b)
...

glibc 维护人员已知此错误:http://sourceware.org/bugzilla/show_bug.cgi?id=13932 - 如果您正在寻找解决方案而不是解决方法,您希望委托具有开源经验的浮点数学专家。

【讨论】:

  • 缩放 xp 在我的测试中没有帮助。修复 glibc 中的问题无济于事,因为这个东西必须在 Mac OS 和 MATLAB 上运行,它使用古老的 GCC 来编译它的 MEX 文件。
【解决方案2】:

64 位 Linux?

使用来自 FreeBSD 的 pow() 代码。

Linux C 库 (glibc) 对于某些输入具有可怕的最坏情况性能。

见:http://entropymine.com/imageworsener/slowpow/

【讨论】:

    【解决方案3】:

    这也可能是您的算法。也许改用 BFGS 之类的东西而不是牛顿的方法会有所帮助。

    您没有提及您的收敛标准。也许这些也需要调整。

    【讨论】:

    • 他没有实现pow,而是使用标准库实现。 :)
    • 不,这实际上正是问题所在。我已经为代码计时并尝试了一切,直到我确定了原因。
    • 我明白了。 BFGS 与您制定矩阵的方式有关,不一定是 pow 计算。
    猜你喜欢
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-28
    • 1970-01-01
    • 2013-04-18
    • 2015-05-22
    相关资源
    最近更新 更多