什么比std :: pow更快?

时间:2013-05-28 01:55:45

标签: c++ performance

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

干杯

4 个答案:

答案 0 :(得分:15)

看起来 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,因为使用memcpy的{​​{3}}应生成相同的代码,并且不会调用未定义的行为。

他还链接到第二个point out in my answer here

第一篇文章还链接到他的微基准测试Optimized pow() approximation for Java, C / C++, and C#

答案 1 :(得分:10)

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

答案 2 :(得分:4)

你的整数有多大?它们在编译时是否已知?将x^2计算为x*x而不是pow(x,2)要好得多。注意:几乎pow()对整数幂的所有应用都涉及将一些数字提高到第二或第三幂(或者在负指数的情况下乘以加数)。在这种情况下使用pow()是过度的。使用模板来表示这些小整数幂,或者只使用x*x

如果整数很小,但在编译时不知道,比如介于-12和+12之间,则乘法仍会超过pow()并且不会失去准确性。您不需要十一次乘法来计算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。

答案 3 :(得分:1)

是的!如果只需要'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
}}