2013-07-26 2 views
9

Мне было интересно, если exp() быстрее, чем общее число pow(). Я быстро запускаю тест на JsPerf http://jsperf.com/pow-vs-exp, и он показал интересные результаты для меня.Pow() vs. exp() performance

Math.exp(logBase * exponent); // fastest 
Math.exp(Math.log(base) * exponent); // middle 
Math.pow(base, exponent); // slowest 

Я знаю, что результаты будут сильно отличаться от архитектуры и языка, но меня также интересует теоретическая точка зрения. Is pow(a, b) реализован как exp(log(a) * b) или есть еще более умный способ, как вычислить силу «прямо» (в C++, C# или JavaScript). Существуют ли инструкции процессора для exp, log или pow на некоторых архитектурах?

Насколько я знаю, и exp(), и log() вычисляются с использованием некоторых серий Тейлора и довольно дороги для вычисления. Это заставляет меня верить, что для постоянной базы власти, этот код

double logBase = log(123.456); 
for (int i = 0; i < 1024; ++i) { 
    exp(logBase * 654.321); 
} 

лучше, чем это

for (int i = 0; i < 1024; ++i) { 
    pow(123.456, 654.321); 
} 

Это правильное предположение?

+4

Я не удивлюсь, если один из этих вариантов был значительно более точным, чем другие. – delnan

+1

У меня есть погрешность около 2-5%. Попробуйте выполнить тесты несколько раз.Но эта контрольная точка, конечно, далека от совершенства. Вот почему меня интересует теория, стоящая за этим. А также точность - интересный вопрос. – NightElfik

+0

Это действительно будет зависеть от деталей реализации. Ваш вопрос о JavaScript конкретно? –

ответ

9

Да, exp будет быстрее, чем pow в целом.

Функции exp и log будут оптимизированы для целевой платформы; многие методы могут быть использованы, например, как Пад аппроксимация, линейным или бинарным сокращение с последующим приближением и т.д.

pow функция обычно реализуются как exp(log(a) * b), как вы говорите, так что это явно медленнее, чем в одиночку exp.Существует множество особых случаев для pow, таких как отрицательные показатели, интегральные показатели, показатели, равные 1/2 или 1/3, и т. Д. В общем случае они замедлят pow, потому что эти тесты являются дорогостоящими.

См. this SO question on pow.

1

В качестве частичного ответа есть инструкции для exp, log или pow на некоторых архитектурах да. Однако это не обязательно означает многое.

Например, на x86 есть

  • f2xm1, который вычисляет 2 х - 1
  • fscale, который вычисляет у * 2 (интермедиат) х
  • fyl2x, который вычисляет у * журнал x
  • fyl2xp1, который вычисляет y * log (x + 1) (имеет ограничения на диапазон ввода)

Однако они мало используются. Он варьируется от архитектуры к архитектуре, но они никогда не бывают быстрыми. В качестве более экстремального примера fyl2x имеет латентность 724 на Sandy Bridge (довольно недавно!), В то время на том же процессоре вы могли бы сделать около 700 независимых добавлений с плавающей запятой или около 240 зависимых добавлений с плавающей запятой или около 2000 независимых простые целочисленные операции.

Это так же плохо, как и получается, но они, как правило, медленные. Медленнее, чтобы ручная реализация могла побить их или, по крайней мере, не сильно проиграть.

Кроме того, код FPU медленно исчезает в пользу кода SSE. Для этих инструкций нет SSE-эквивалентов.

+0

SSE воплощает FPU, и он уже в значительной степени заменил код * x87 *, поэтому Intel не прилагает усилий к созданию 'fyl2x' быстро. – Potatoswatter

+0

@Potatoswatter вы, кажется, предположили, что вызов кода x87 «код FPU» не является правильным, однако это обычно делается. Это допустимое различие, SSE не использует старый FPU. Конечно, он реализован с теми же функциональными блоками на процессоре, но по логике они полностью разделены. – harold

+0

Это может быть или не быть. В любом случае SSE представляет собой набор команд для (векторного) FPU, поэтому вызов кода x87 «код FPU», чтобы отличить его от SSE, вводит в заблуждение или просто запутывает. Более того, x87 не имеет значения, поскольку он устарел. – Potatoswatter

1

Независимо от деталей архитектуры, Math.pow должен делать больше с точки зрения проверки ошибок (например, что происходит, если база отрицательна?). чем Math.exp (и поэтому я ожидал бы, что pow будет медленнее).

Соответствующие части спецификации:

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.8

15.8.2.8 ехр (х)

Возвращает зависящую от реализации приближения к экспоненциальной функции от й (е, возведенного в мощность x, где e - основание натуральных логарифмов ).

Если x является NaN, результатом является NaN. Если x равно +0, результат равен 1. Если x равно -0, результат равен 1. Если x равно + ∞, результат равен + ∞. Если x равно -∞, результат равен +0.

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.13

15.8.2.13 пау (х, у)

Возвращает зависящее от реализации приближение к результату подъема х к мощности у.

Если y является NaN, результатом является NaN. Если y равно +0, результат равен 1, даже если x - NaN. Если y равно -0, результат равен 1, даже если x является NaN. Если x является NaN и y отличен от нуля, результатом является NaN. Если abs (x)> 1 и y равно + ∞, результат равен + ∞. Если abs (x)> 1 и y равно -∞, результат равен +0. Если abs (x) == 1 и y + ∞, результатом является NaN. Если abs (x) == 1 и y равно -∞, результат равен NaN. Если abs (x) < 1 и y равно + ∞, результат равен +0. Если abs (x) < 1 и y is -∞, результат равен + ∞. Если x равно + ∞ и y> 0, результат равен + ∞. Если x равно + ∞ и y < 0, результат равен +0. Если x - -∞ и y> 0, а y - нечетное целое число, результат равен -∞. Если x - -∞ и y> 0, а y не является нечетным целым числом, результат равен + ∞. Если x is -∞ и y < 0, а y - нечетное целое число, результат равен -0. Если x is -∞ и y < 0, а y не является нечетным целым числом, результатом является +0. Если x равно +0 и y> 0, результат равен +0. Если x равно +0 и y < 0, результат равен + ∞. Если x равно -0 и y> 0, а y - нечетное целое число, результат равен -0. Если x равно -0 и y> 0, а y не является нечетным целым числом, результатом является +0. Если x - -0 и y < 0, а y - нечетное целое число, результат равен -∞. Если x равно -0 и y < 0, а y не является нечетным целым числом, то результат равен + ∞. Если x и x конечен, а y конечен и y не является целым числом, результатом является NaN.

+0

Спасибо, это интересно! Вероятно, поэтому 'Math.exp (Math.log (base) * exponent)' быстрее, чем 'Math.pow (base, exponent)'. Это может не правильно вычислять все особые случаи, но я все равно не забочусь о них. – NightElfik

+0

@NightElfik независимо от ответа на вопрос, если вы не планируете делать тяжелую работу с числами в JS, я сомневаюсь, что время выполнения 'exp' и' pow' доминирует над временем выполнения вашего скрипта. Помните цитату из текста: «преждевременная оптимизация - это корень всего зла» – SheetJS

+0

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