2013-06-27 3 views
1

От моего друга я слышал, что функция pow медленнее, чем его эквивалент, просто умножая базу на себя, количество раз в качестве ее показателя. Например, по его словам,Почему функция pow более медленная, чем простые операции?

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

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

медленнее, чем

#include <stdio.h> 

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

Как новичок, я думаю, они оба компилировать с той же скоростью, и по той же логике, я предпочел бы бывший за его типичную содержательность. Итак, почему прежний блок кода медленнее, чем последний?

+0

Потому что умножение 'double' является дешевым, делая то, что' pow' does not (вычисляет корни для десятичных степеней). –

+0

Кроме того, «компиляция с той же скоростью»! = «Работает с одинаковой скоростью». Компиляция - это * очень * отдельный процесс от фактического запуска кода. –

+0

@ RichardJ.RossIII: Я думаю, что «десятичным» вы на самом деле называете «дробным»; например, 'pow (1.2, 3.4)', что не может быть сделано простой последовательностью умножений. –

ответ

5

pow(double,double) необходимо регулировать подъем до любой мощностью, а не только на основе целых оснований или, особенно, 2. Таким образом, это намного сложнее, чем простое умножение двух двойных значений.

3

Поскольку функция pow должны реализовать более общий алгоритм, который должен работать на всех случаях (в частности, он должен быть в состоянии поднять до любого рационального показателя представимой double), в то время как e*e только простой умножение, которое сводится к одной или двум инструкциям сборки.

По-прежнему, если компилятор достаточно умен, он может автоматически автоматически заменить ваш pow(e, 2.0)e*e (ну, на самом деле, в вашем случае он, вероятно, просто выполнит весь расчет во время компиляции).


Просто для удовольствия, я провел несколько тестов: составление следующего кода

#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; 
} 

с g++ -O3 -c pow.c (г ++ 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  

Итак, когда компилятор уже знал все задействованные значения, он просто выполнял вычисления во время компиляции; и для обоих pow2 и valuexvalue он выбрал один mulsd xmm0,xmm0 (т. е. в обоих случаях он сводится к умножению значения с самим собой в одной команде сборки).

+0

На самом деле, я не совсем уверен, что компилятор может встроить что-то подобное, особенно если вы динамически связываетесь с этой функцией. Он может иметь результаты в некоторых краевых случаях, которые отличаются от простого умножения. –

+0

@ RichardJ.RossIII: s/will/may/:) –

+1

@ RichardJ.RossIII pow() является частью стандартной библиотеки (C99 7.12.7).Компилятору позволено предположить, что вы используете ** его функцию ** pow(). Использование другой функции с именем 'pow' - это неопределенное поведение. –

0

Here is one (simple, heed the comment) pow implementation. В общем случае он включает в себя несколько ветвей потенциального деления и вызывает exp, log, modf.

С другой стороны, умножение - это одна команда (дайте или возьмите) на большинстве более высоких ЦП.