2013-07-26 5 views
9

exp()がより一般的なものよりも速いのかどうか疑問に思っていましたpow()。私はJsPerf http://jsperf.com/pow-vs-expで高速ベンチマークを実行し、私にとって興味深い結果を示しました。Pow()とexp()のパフォーマンス

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

私は結果がアーキテクチャと言語によって大きく異なることを知っていますが、私は理論的観点にも興味があります。 pow(a, b)exp(log(a) * b)として実装されていますか、またはC++やC#やJavaScriptなどで "直接"どのように力を計算するかがもっと巧妙な方法です。いくつかのアーキテクチャーで、exp、log、またはpowのCPU命令がありますか?

私が知る限り、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よりも遅くなります。負の指数、積分指数、1/2または1/3に等しい指数など、powの多くの特別なケースがあります。これらのテストは高価であるため、一般的なケースではさらに遅くなります。pow

this SO question on powを参照してください。

1

一部の回答では、いくつかのアーキテクチャでexp、log、またはpowの指示があります。しかし、それは必ずしも多くを意味しません。

例えば、x86で算出

  • f2xm1あります2 X - yは* ログ計算のy * 2 (INT)X
  • fyl2xを算出する1
  • x
  • fyl2xp1 y * log (x + 1)(入力範囲に制限があります)

ただし、あまり使用されていません。それはアーキテクチャによって異なりますが、決して高速です。より極端な例として、fyl2xはSandy Bridge(かなり最近!)に724のレイテンシを持っています。その時、同じプロセッサ上で、約700の独立した浮動小数点の加算、または約240の従属浮動小数点の加算、単純な整数演算。

これはそれほど悪いことですが、通常は遅いです。手動の実装がそれらを打ち負かすことができるか、少なくとも大幅には失われないほど遅くなります。

また、FPEコードはSSEコードのために徐々に消えています。これらの命令に相当するSSEはありません。

+0

SSEはFPUを具現しており、* x87 *コードを既に大幅に置き換えているため、Intelは 'fyl2x'を高速化するための努力をしていません。 – Potatoswatter

+0

@Potatoswatterあなたはx87コード "FPUコード"を呼び出すことは適切ではないことを示唆しているようですが、これは一般的に行われています。これは有効な区別ですが、SSEは古いFPUを使用しません。もちろん、CPU上の同じ機能ユニットで実装されていますが、論理的には完全に別です。 – harold

+0

そうかもしれません。いずれにしても、SSEは(ベクトル)FPUの命令セットであるため、SSEと区別するためにx87コード「FPUコード」を呼び出すことは誤解を招くか、混乱させるだけです。さらに、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 EXP(X)

にまで上昇させ、xの指数関数 (Eに実装依存の近似値を返します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 POW(x、y)は

パワーyにXを上げる 結果に実装依存の近似値を返します。

yがNaNの場合、結果はNaNになります。 yが+0の場合、x がNaNであっても結果は1になります。 yが-0の場合、xがNaNであっても結果は1になります。 xがNaNで、 yが0以外の場合、結果は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が-∞の場合、 の結果は+∞になります。 xが+∞でy> 0の場合、結果は+∞になります。 xが+∞で、 y <の場合、結果は+0です。 xが-∞、y> 0、yが奇数の場合、 の結果は-∞です。 xが-∞でy> 0でyが奇数でない場合、 の結果は+∞になります。 xが-∞で、yが<、yが奇数の場合、結果は で、-0です。 xが-∞でy<が0で、yが奇数でない場合、結果は +0です。 xが+0かつy> 0の場合、結果は+0になります。 xが+0かつy <の場合、結果は+∞になります。 xが-0、y> 0、yが奇数の場合、結果は です。 xが-0、y> 0、yが奇数でない場合、結果は +0です。 xが-0かつy < 0でyが奇数の場合、結果は-∞です。 xが-0で、yが<でyが奇数でない場合、結果は+∞になります。 x であり、xが有限であり、yが有限であり、yが整数でない場合、結果は NaNです。

+0

ありがとう、これは面白いです!それはたぶん 'Math.exp(Math.log(base)*指数)'が 'Math.pow(base、exponent)'よりも速い理由でしょう。それは、すべての特殊なケースを正しく計算するわけではないかもしれませんが、とにかく私はそれらについて気にしません。 – NightElfik

+0

@NightElfik質問に対する答えにかかわらず、JSで大量の数値作業を行う予定がない限り、私は 'exp'と' pow'の実行時間があなたのスクリプトの実行時間を支配するのではないかと疑います。 "早産最適化はすべての悪の根源です" – SheetJS

+0

私はこれを非常によく知っています。コードのこの部分が頻繁に使用されていますが、実際にコードをスピードアップしようとするよりも、この現象の背景にある理論についてもっと迷っていました。パフォーマンスを最適化する前にまずプロファイリングを行います。 – NightElfik