【问题标题】:What is faster than std::pow?什么比 std::pow 更快?
【发布时间】:2013-05-22 21:00:12
【问题描述】:

我的程序在 std::pow(double,int) 函数上花费了 90% 的 CPU 时间。准确性不是这里的主要关注点,所以我想知道是否有更快的替代方案。我想尝试的一件事是强制浮动,执行操作,然后返回双倍(还没有尝试过);我担心这不是一种提高性能的可移植方式(大多数 CPU 本质上不是在双打上运行吗?)

干杯

【问题讨论】:

  • 有点取决于您计算的功率 - 请显示一些代码和/或描述您的数据。
  • 在一般情况下,硬件比软件快,这就是pow 的意义所在……除非你对你正在做的事情施加额外的限制,否则你无法击败它。
  • 这篇文章可能有用:martin.ankerl.com/2012/01/25/…
  • 如果您知道要提高到幂的数字范围,并且如果您使用有限数量的幂,那么您可以通过将预先计算的值存储到数组中来获得出色的优化并简单地使用指数作为索引通过索引获取它们。

标签: c++ performance


【解决方案1】:

好像Martin Ankerl 有几篇关于这方面的文章,Optimized Approximative pow() in C / C++ 是其中之一,它有两个快速版本,一个如下:

inline double fastPow(double a, double b) {
  union {
    double d;
    int x[2];
  } u = { a };
  u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
  u.x[0] = 0;
  return u.d;
}

它依赖于通过联合进行类型双关语,这是 C++ 中未定义的行为,来自草案标准部分 9.5[class.union]:

在一个联合中,在任何时候最多可以有一个非静态数据成员处于活动状态,即at的值 大多数非静态数据成员可以随时存储在联合中。 [...]

但大多数编译器包括gcc support this with well defined behavior:

从不同的工会成员阅读而不是最近写信的人(称为“类型双关语”)的做法很常见。即使使用 -fstrict-aliasing,也允许类型双关,前提是通过联合类型访问内存

但这不是通用的,因为 this article points out 和我 point out in my answer here 使用 memcpy 应该生成相同的代码并且不会调用未定义的行为。

他还链接到第二个Optimized pow() approximation for Java, C / C++, and C#。

第一篇文章还链接到他的微基准here

【讨论】:

    【解决方案2】:

    根据您需要做什么,在对数域中操作可能会起作用——也就是说,您将所有值替换为它们的对数;乘法变成加法,除法变成减法,取幂变成乘法。但是现在加法和减法变得昂贵并且有些容易出错。

    【讨论】:

      【解决方案3】:

      你的整数有多大?它们在编译时已知吗?将x^2 计算为x*x 比pow(x,2) 好得多。注意:几乎所有pow() 对整数幂的应用都涉及将某个数字提高到二次或三次幂(或负指数情况下的乘法逆运算)。在这种情况下使用pow() 是多余的。为这些小整数幂使用模板,或者只使用x*x。

      如果整数很小,但在编译时未知,例如在 -12 和 +12 之间,乘法仍然会超过 pow(),并且不会失去准确性。您不需要 11 次乘法来计算 x^12。四个就可以了。使用 x^(2n) = (x^n)^2 和 x^(2n+1) = x*((x^n)^2) 的事实。例如,x^12 是 ((x*x*x)^2)^2。两次乘法计算 x^3 (x*x*x),再乘一次计算 x^6,最后一次乘法计算 x^12。

      【讨论】:

      • 当然,这假设 ausairman 正在使用整数。目前还不清楚是否是这种情况。
      • @jamesdin:他当然是。 我的程序在 std::pow(double,int) 函数中花费了 90% 的 CPU 时间。
      • 糟糕,抱歉。你说得对;我想我的大脑今天在度假。 >_
      • 这个答案在最近的优化器中是错误的。 std::pow(x, {2, 3, 4})... 编译为与相应乘法完全相同的代码。
      • @Jean-MichaëlCelerier ICC on godbolt 做得很好。但是 GCC 和 Clang 只对 2 执行此操作,如果您不指定 -Ofast,则调用 pow 以获得更大的指数。
      【解决方案4】:

      是的!如果您只需要 'y'/'n' 作为 long/int,则速度非常快,这样您就可以避免缓慢的 FPU FSCALE 函数。如果您只需要 'y'/'n' 作为 INT 的结果,这是 Agner Fog 的 x86 手动优化版本。我将它升级到 __fastcall/__declspec(naked) 以获得速度/大小,利用 ECX 传递 'n'(对于 32 位 MSVC++,浮点数总是在堆栈中传递),所以我的调整非常小,主要是 Agner 的工作.它是在 MS Visual VC++ 2005 Express/Pro 上测试/调试/编译的,所以应该可以滑入较新的版本。对通用 CRT pow() 函数的准确性非常好。

      extern double __fastcall fs_power(double x, long n);
      
      // Raise 'x' to the power 'n' (INT-only) in ASM by the great Agner Fog!
      
      __declspec(naked) double __fastcall fs_power(double x, long n) { __asm {
          MOV  EAX, ECX     ;// Move 'n' to eax
      ;// abs(n) is calculated by inverting all bits and adding 1 if n < 0:
          CDQ               ;// Get sign bit into all bits of edx
          XOR  EAX, EDX     ;// Invert bits if negative
          SUB  EAX, EDX     ;// Add 1 if negative. Now eax = abs(n)
          JZ   RETZERO      ;// End if n = 0
          FLD1              ;// ST(0) = 1.0 (FPU push1)
          FLD  QWORD PTR [ESP+4] ;// Load 'x' : ST(0) = 'x', ST(1) = 1.0 (FPU push2)
          JMP  L2           ;// Jump into loop
      L1: ;// Top of loop
          FMUL ST(0), ST(0) ;// Square x
      L2: ;// Loop entered here
          SHR  EAX, 1       ;// Get each bit of n into carry flag
          JNC  L1           ;// No carry. Skip multiplication, goto next
          FMUL ST(1), ST(0) ;// Multiply by x squared i times for bit # i
          JNZ  L1           ;// End of loop. Stop when nn = 0
          FSTP ST(0)        ;// Discard ST(0) (FPU Pop1)
          TEST EDX, EDX     ;// Test if 'n' was negative
          JNS  RETPOS       ;// Finish if 'n' was positive
          FLD1              ;// ST(0) = 1.0, ST(1) = x^abs(n)
          FDIVR             ;// Reciprocal
      RETPOS:    ;// Finish, success!
          RET  4 ;//(FPU Pop2 occurs by compiler on assignment
      
      RETZERO:
          FLDZ   ;// Ret 0.0, fail, if n was 0
          RET  4
      }}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-07
        • 1970-01-01
        • 2014-10-24
        • 2021-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多