2013-02-04 2 views
5

f(x) = 0 방정식을 풀 수있는 수치 코드를 가지고 있습니다. 을 힘으로 p까지 올려야합니다. 나는 그것을 여러 가지 방법으로 풀지 만 결국에는 뉴튼의 방법을 사용한다. 솔루션은 x = 1과 같아 지므로 내 문제의 원인이됩니다. 반복 된 솔루션이 1, 즉 x = 1 + 1e-13에 가까워지면 std::pow(x, p)을 계산하는 데 필요한 시간이 100 배로 커져서 내 코드를 사용할 수 없게됩니다.매우 느린 std :: pow()는 매우 가까운 기지에 대해 1

이 항목을 실행하는 컴퓨터는 CentOS에서 AMD64 (Opteron 6172)이고 명령은 간단합니다. . 비슷한 동작이 모든 x64의 모든 컴퓨터에 나타납니다. here과 마찬가지로이 문제는 내 문제 일뿐만 아니라 x64에만 표시되며 x 근처에는 1.0에 불과합니다. 비슷한 일이 exp에 발생합니다.

이 문제를 해결하는 것이 중요합니다. 이 속도 저하를 피할 수있는 방법이 있는지 아는 사람이 있습니까?

편집 : John은 denormals 때문이라고 지적했습니다. 그러면 문제는 어떻게 해결할 수 있습니까? 코드는 GNU Octave 내에서 사용하기 위해 g++으로 컴파일 된 C++입니다. -mtune=native-ffast-math을 포함하도록 CXXFLAGS을 설정 했는데도 느리게 실행되고 도움이되지 않는 것 같습니다.

지금 문제 해결 :이 문제에 관심이있는 모든 사람들에게 아래 제안 된 해결책은 개인적으로 저에게 효과가 없었습니다. 나는 보통 std::pow()의 보통 속도를 필요로하지만, 느린 것없이 x = 1을하지 않아도된다. 나를 위해이 솔루션은 개인적으로 다음과 같은 해킹을 사용하는 것입니다

inline double mpow(double x, double p) __attribute__ ((const)); 

inline double mpow(double x, double p) 
{ 
    double y(x - 1.0); 
    return (std::abs(y) > 1e-4) ? (std::pow(x, p)) : (1.0 + p * y * (1.0 + (p - 1.0) * y * (0.5 + (1.0/6.0) * (p - 2.0) * y))); 
} 

바운드가 변경 될 수 있습니다,하지만 -40 < 페이지 < (40)에 대한 오류 약 1E-11, 충분하다보다 작습니다. 오버 헤드는 내가 발견 한 것으로부터 최소이며 따라서이 문제가 해결됩니다.

+5

이는 [정상 이하의 숫자]와 일반적인 성능 문제와 관련 될 수있다 (http://en.wikipedia.org/wiki/Denormal_number). 부동 소수점 값이 0에 매우 가까운 계산은 평소보다 100 배 느려질 수 있습니다. http://stackoverflow.com/questions/9314534/why-does-changing-0-1f-to-0-slow-down-performance-by-10x를 참조하십시오. –

+0

좋은 지적. 이 문제를 해결하는 방법에 대한 제안? 그들이 충분히 가까울 경우 숫자를 정확하게 1로 고정하십시오. –

+0

@JohnKugelman : 링크를 읽는다면, glibc는 특정 입력 값이 주어지면 훨씬 느린 함수 ('__slowpow ')를 사용하기 때문입니다. – interjay

답변

0

알고리즘 일 수도 있습니다. 아마도 Newton의 방법 대신 BFGS와 같은 것으로 전환하는 것이 도움이 될 것입니다.

수렴 기준에 대해서는 아무 것도 말하지 않습니다. 어쩌면 조정할 필요가 있을지도 모릅니다.

+0

그는'pow'을 구현하지 않고 표준 라이브러리 구현을 사용합니다. :) – jalf

+1

아니요, 실제로 이것은 정확하게 문제입니다. 원인을 밝히기 전까지 코드를 조사하고 모든 것을 시도했습니다. –

+0

알겠습니다. BFGS는 행렬을 공식화하는 방식과 관련이 있습니다. 반드시 멱 계산식이 아니어야합니다. – duffymo

9

실제 해결 방법은 a ** b == exp(log(a) * b)이며 그 대신 양식을 사용하는 것입니다. 결과의 정확성에 나쁜 영향을 미치지 않는지 확인해야합니다. 편집 : 토론 된 바와 같이, 이것은 또한 거의 같은 정도의 속도 저하로 어려움을 겪습니다.

문제는 적어도 직접적으로 비정규 화되지는 않습니다. exp(-2.4980018054066093e-15)을 계산하려고하면 같은 속도 저하가 발생하고 -2.4980018054066093e-15는 확실히 비정규가 아닙니다.

그런 다음 exponend 또는 저속 영역 외부로 가야 지수 중 하나를 확장, 결과의 정확성에 대해 상관하지 않는 경우 :이 버그는 glibc에 테이너로 알려져있다

sqrt(pow(a, b * 2)) 
pow(a * 2, b)/pow(2, b) 
... 

: http://sourceware.org/bugzilla/show_bug.cgi?id=13932을 - 해결 방법과 달리 수정 프로그램을 찾고 있다면 오픈 소스 경험이있는 부동 소수점 수학 전문가에게 위임을 원할 것입니다.

+0

이것은 pow()보다 약 4 배 빠릅니다. –

+0

'x' 또는'p' 스케일링은 테스트에서 도움이되지 않았습니다. glibc에서이 문제를 해결하는 것은 도움이되지 않습니다. 왜냐하면이 작업은 고대 GCC를 사용하여 MEX 파일을 컴파일하는 Mac OS 및 MATLAB에서 실행되어야하기 때문입니다. –

1

64 비트 Linux?

FreeBSD의 pow() 코드를 사용하십시오.

Linux C 라이브러리 (glibc)는 특정 입력에 대해 끔찍한 최악의 성능을 나타냅니다.

참조 : http://entropymine.com/imageworsener/slowpow/