简体   繁体   English

为什么pow函数比简单操作慢?

[英]Why is the pow function slower than simple operations?

From a friend of mine, I heard that the pow function is slower than its equivalent in simply multiplying the base by itself, the amount of times as its exponent. 从我的一个朋友那里,我听说pow函数比简单地乘以基数(即乘以其指数的次数)要慢得多。 For example, according to him, 例如,据他说,

#include <stdio.h>
#include <math.h>

int main () {
    double e = 2.71828
    e2 = pow (e, 2.0)
    printf("%le", e2)
}

is slower than 比慢

#include <stdio.h>

int main() {
    double e = 2.71828
    e2 = e * e
    printf("%le", e2)
}

As a novice, I would think they both compile at the same speed, and by the same logic, I would prefer the former for its typical pithiness. 作为新手,我认为它们的编译速度和逻辑相同,因此我更喜欢前者的典型信奉度。 So, why is the former block of code slower than the latter one? 那么,为什么前一个代码块比后一个代码块慢?

pow(double,double) needs to handle raising to any power , not just an integer based power, or especially 2 . pow(double,double)需要处理任何幂 ,而不仅仅是基于整数的幂,尤其是2 As such, it's far more complicated than just doing a simple multiplication of two double values. 这样,它比仅对两个double值进行简单的乘法要复杂得多。

Because the pow function must implement a more generic algorithm that has to work on all the cases (in particular, it must be able to elevate to any rational exponent representable by a double ), while e*e is just a simple multiplication that will boil down to one or two assembly instructions. 因为pow函数必须实现一个必须在所有情况下都可以使用的更通用的算法(特别是,它必须能够提升为double表示的任何有理指数),而e*e只是一个简单的乘法,它将沸腾精简到一两个汇编指令。

Still, if the compiler is smart enough, it may automatically replace your pow(e, 2.0) with e*e automatically anyway (well, actually in your case it will probably just perform the whole computation at compile time). 不过,如果编译器足够聪明,它可能会自动以e*e自动替换pow(e, 2.0) (嗯,实际上,在您的情况下,它可能只会在编译时执行整个计算)。


Just for fun, I ran some tests: compiling the following code 只是为了好玩,我进行了一些测试:编译以下代码

#include <math.h>

double pow2(double value)
{
    return pow(value, 2.);
}

double knownpow2()
{
    double e=2.71828;
    return pow(e, 2.);
}

double valuexvalue(double value)
{
    return value*value;
}

double knownvaluexvalue()
{
    double e=2.71828;
    return e*e;
}

with g++ -O3 -c pow.c (g++ 4.7.3) and disassembling the output with objdump -d -M intel pow.o I get: 使用g++ -O3 -c pow.c (g ++ 4.7.3)并使用objdump -d -M intel pow.o分解输出,我得到:

0000000000000000 <_Z4pow2d>:
   0:   f2 0f 59 c0             mulsd  xmm0,xmm0
   4:   c3                      ret    
   5:   66 66 2e 0f 1f 84 00    data32 nop WORD PTR cs:[rax+rax*1+0x0]
   c:   00 00 00 00 

0000000000000010 <_Z9knownpow2v>:
  10:   f2 0f 10 05 00 00 00    movsd  xmm0,QWORD PTR [rip+0x0]        # 18 <_Z9knownpow2v+0x8>
  17:   00 
  18:   c3                      ret    
  19:   0f 1f 80 00 00 00 00    nop    DWORD PTR [rax+0x0]

0000000000000020 <_Z11valuexvalued>:
  20:   f2 0f 59 c0             mulsd  xmm0,xmm0
  24:   c3                      ret    
  25:   66 66 2e 0f 1f 84 00    data32 nop WORD PTR cs:[rax+rax*1+0x0]
  2c:   00 00 00 00 

0000000000000030 <_Z16knownvaluexvaluev>:
  30:   f2 0f 10 05 00 00 00    movsd  xmm0,QWORD PTR [rip+0x0]        # 38 <_Z16knownvaluexvaluev+0x8>
  37:   00 
  38:   c3                      ret    

So, where the compiler already knew all the values involved it just performed the computation at compile-time; 因此,在编译器已经知道所有涉及的值的地方,它只是在编译时执行了计算。 and for both pow2 and valuexvalue it emitted a single mulsd xmm0,xmm0 (ie in both cases it boils down to the multiplication of the value with itself in a single assembly instruction). 对于pow2valuexvalue它发出单个mulsd xmm0,xmm0 (即,在两种情况下,都归结为在单个汇编指令中值与自身的乘积)。

Here is one (simple, heed the comment) pow implementation . 这是一个(简单,请注意注释)pow实现 In being generic it involves a number of branches a potential division and calls to exp, log, modf .. 在泛型中,它涉及多个分支以及一个潜在的分支,并调用exp,log,modf ..

On the other hand, on the multiplication is a single instruction (give or take) on most higher CPUs. 另一方面,在大多数更高的CPU上,乘法是一条指令(给定或取指令)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM