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