2013-07-26 2 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()은 일부 테일러 (Taylor) 시리즈를 사용하여 계산되며 계산하는 데 꽤 비쌉니다. 이 날

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 및기능은 대상 플랫폼에 맞게 최적화됩니다. 많은 기술 등 당신이 말하는대로 pow 기능은 일반적으로 exp(log(a) * b)로 구현됩니다

Pade 근사 근사 다음 선형 또는 이진 감소로 사용할 수 있습니다, 그래서 그것은 분명히 혼자 exp보다 느립니다.적분 지수, 지수가 1/2 또는 1/3 등등과 같은 많은 특별한 경우가 pow입니다. 이러한 테스트는 비용이 많이 들기 때문에 일반적인 경우보다 더 느려질 수 있습니다. pow

this SO question on pow을 참조하십시오.

1

일부 대답으로, 일부 아키텍처에서는 exp, log 또는 pow에 대한 지침이 있습니다. 그러나 이것이 반드시 많은 의미는 아닙니다.

예를 들어, 86에서 산출

  • f2xm1 거기 2 X - Y는 * 로그 계산의 Y * 2 (INT) ×
  • fyl2x을 계산 한
  • fscale 2 x
  • fyl2xp1 y * log (x + 1) (입력 범위에 제한이 있음)

그러나 많이 사용되지는 않습니다. 아키텍처에 따라 다르지만 결코 빠르지는 않습니다. 보다 극단적 인 예로서 fyl2x은 Sandy Bridge에서 724의 레이턴시를 가지고 있습니다. (꽤 최근입니다!) 같은 프로세서에서 약 700 개의 독립적 인 부동 소수점 추가 또는 약 240 개의 종속 부동 소수점 추가 또는 약 2000 개의 독립적 인 간단한 정수 연산.

대략 나쁘지 만 일반적으로 느립니다. 수동 구현으로 인해 성능이 저하되거나 적어도 크게 상실하지는 않는다.

또한 FPU 코드는 SSE 코드에 찬성하여 서서히 사라지고 있습니다. 해당 지침에 해당하는 SSE는 없습니다.

+0

SSE는 FPU를 구현하며, 이미 * x87 * 코드를 대체하여 인텔은'fyl2x '를 빨리 만들지 않습니다. – Potatoswatter

+0

@Potatoswatter 당신은 x87 코드 "FPU code"를 호출하는 것이 일반적으로 적절하지 않다고 제안하는 것 같습니다. SSE는 구형 FPU를 사용하지 않고 유효한 구별법입니다. 물론 CPU에서 동일한 기능 유닛으로 구현되었지만 논리적으로는 완전히 별개입니다. – 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가 EXP (x)는

는 X (E의 지수 기능 구현 의존적 근사치를 돌려

15.8.2.8은 제곱 여기서 x는 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가 <이고 y가 홀수가 아닌 경우 결과는 +0입니다. x가 +0이고 y> 0이면 결과는 +0입니다. x가 +0이고 y가 <이면 결과는 + ∞입니다. x가 -0이고 y> 0이고 y가 홀수 인 경우 결과는 이며 -0입니다. x가 -0이고 y> 0이고 y가 홀수가 아닌 경우 결과는 +0입니다. x가 -0이고 y가 <이고 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'의 실행 시간이 스크립트의 실행 시간을 지배하는 것이 아닌지 의심 스럽습니다. knuth quote를 기억하십시오. "조기 최적화는 모든 악의 뿌리입니다." – SheetJS

+0

나는 이것을 아주 잘 압니다. 이 코드의 일부가 많이 사용 되긴했지만, 필자는 실제로 코드를 빠르게하기보다는이 현상의 이론에 대해 더 많이 배회했다. 성능 최적화를하기 전에 먼저 프로파일 링을 수행합니다. – NightElfik