Pow (x, p) быстрее, когда показатель степени является целым числом? - PullRequest
2 голосов
/ 22 апреля 2020

В коде, использующем pow(double x, double p) (большая часть случаев имеет p = 2.0), я заметил, что выполнение моего кода явно быстрее, когда p = 2.0, чем когда p = 2.000000001. Я заключаю, что на моем компиляторе (g cc 4.8.5) реализация pow определяет, когда это квадрат во время выполнения.

Следуя этому наблюдению, я заключаю, что мне не нужен конкретная реализация c, когда я знаю, что p равно 2. Но мой код должен быть кроссплатформенным, тогда мой вопрос:

оптимизирован pow, когда показатель степени является целым числом в большинстве компиляторы c ++ 03?

В моем текущем контексте "большая часть компилятора" = "g cc> = 4.8, intel с msv c, intel на unix "

Ответы [ 2 ]

0 голосов
/ 22 апреля 2020

Да, стандартные библиотеки пытаются выполнить оптимизацию во время выполнения, если экспонента определена как натуральное число. Глядя на текущую версию glib c i386 версии POW, вы можете найти следующий код.

    /* First see whether `y' is a natural number.  In this case we
       can use a more precise algorithm.  */
    fld %st     // y : y : x
    fistpll (%esp)      // y : x
    fildll  (%esp)      // int(y) : y : x
    fucomp  %st(1)      // y : x
    fnstsw
    sahf
    jne 3f

, встроенный в реализацию. Полный код можно найти по адресу github .

Обратите внимание, что для других версий glib c и других архитектур ответ может отличаться.

0 голосов
/ 22 апреля 2020

РЕДАКТИРОВАТЬ

Ответ ниже мини-интерпретирует вопрос ОП, который был конкретно о RUNTIME оптимизации, тогда как я исследовал оптимизацию времени компиляции.

Оригинальный ответ

Добавление к моему комментарию. До тех пор, пока у экспоненты будет int меньше или равно MAXINT, вы получите.

#include <cmath>

double pow(double a)
{
    return std::pow(a, (int)2147483647);
}

генерирует

pow(double):
        movapd  xmm4, xmm0
        mulsd   xmm4, xmm0
        movapd  xmm5, xmm4
        mulsd   xmm5, xmm4
        mulsd   xmm4, xmm0
        movapd  xmm6, xmm5
        mulsd   xmm4, xmm5
        mulsd   xmm6, xmm5
        movapd  xmm3, xmm6
        mulsd   xmm3, xmm6
        mulsd   xmm3, xmm0
        movapd  xmm0, xmm4
        movapd  xmm2, xmm3
        movapd  xmm1, xmm3
        mulsd   xmm2, xmm6
        mulsd   xmm1, xmm3
        mulsd   xmm2, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm2
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm4
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm1
        mulsd   xmm0, xmm1
        ret

, но вы должны быть осторожны использовать int литерал

#include <cmath>

double pow(double a)
{
    return std::pow(a, (unsigned int) 2147483647);
}

создает

pow(double):
        movsd   xmm1, QWORD PTR .LC0[rip]
        jmp     pow
.LC0:
        .long   4290772992
        .long   1105199103

РЕДАКТИРОВАТЬ

Кажется, я не прав. Выше был протестирован с ранней версией G CC. В ранних версиях G CC и CLANG умножение встроено. Однако в более поздних версиях этого не происходит. Вполне возможно, что более новые версии Если вы переключите версии на Godbolt, то вы увидите, что вышеприведенное НЕ ПРОИСХОДИТ.

Например,

#include <cmath>

double pow_v2(double a)
{
    return std::pow(a, 2);
}

double pow_v3(double a)
{
    return std::pow(a, 3);
}

для CLANG 10.0 генерирует

pow_v2(double):                             # @pow_v2(double)
        mulsd   xmm0, xmm0
        ret
.LCPI1_0:
        .quad   4613937818241073152     # double 3
pow_v3(double):                             # @pow_v3(double)
        movsd   xmm1, qword ptr [rip + .LCPI1_0] # xmm1 = mem[0],zero
        jmp     pow                     # TAILCALL

, но для CLANG 5.0 он генерирует

pow_v2(double):                             # @pow_v2(double)
        mulsd   xmm0, xmm0
        ret
pow_v3(double):                             # @pow_v3(double)
        movapd  xmm1, xmm0
        mulsd   xmm1, xmm1
        mulsd   xmm1, xmm0
        movapd  xmm0, xmm1
        ret

Кажется, что для более поздних версий компиляторов функция intrinsi c pow вызывается быстрее, чем встроенные умножения, поэтому компиляторы меняют свою стратегию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...