2008-09-15 9 views
10

자바에서 매우 자주 Math.exp()을 계산해야합니다. 보다 빠르게 실행하는 네이티브 버전을 얻을 수 있습니까? 님의 Math.exp() ??JNI를 통해 Math.exp()가 더 빠릅니까?

나는 단지 jni + C를 시도했지만, 평범한 것보다 느리다. 자바.

+0

Math.exp()와 JNI 버전의 정확한 시간을 얻기 위해 성능 테스트를 수행 했습니까? JIT의 효과를보기 위해 10k 번 호출 된 후에는 어떻습니까? – martinatime

+0

이것은 JVM에 달려 있지만 보통'Math.exp' *는 C *에서 구현됩니다. 당신은 빠른 알고리즘을 사용하는 것이 좋습니다. – Joni

답변

11

+1 자신의 exp() 구현 작성. 즉, 응용 프로그램에서 이면 실제로 병목 현상이 발생합니다. 약간의 부정확성을 처리 할 수 ​​있다면 매우 효율적인 지수 추정 알고리즘이 많이 있습니다. 그 중 일부는 수세기를 거슬러 올라갑니다. Java의 exp() 구현은 "정확한"결과를 반환해야하는 알고리즘의 경우에도 상당히 느립니다.

오, 순수 자바에서 해당 exp() 구현을 작성하는 것을 두려워하지 마십시오. JNI는 많은 오버 헤드를 가지고 있으며 JVM은 런타임시 바이트 코드를 C/C++이 달성 할 수있는 것 이상으로 최적화 할 수 있습니다.

+0

두 가지 중요한 사항은 다음과 같습니다. (1) JNI 오버 헤드가 다른 모든 고려 사항보다 중요 할 수 있습니다. (2) JVM JIT는 기계가 충분히 따뜻해지는 한 작은 방법을 최적화 할 때 놀랍게도 (때때로 C/C++과 같거나 빠름) 좋습니다. – kevinarpe

6

자바를 사용하십시오.

또한 exp 결과를 캐시 한 다음 다시 계산하는 것보다 빠르게 답을 찾을 수 있습니다.

+0

어떻게 결과를 캐시합니까? 캐싱은 상당히 비쌀 수 있습니다. HashMap을 사용해 보았는데 간단히 계산 만하는 것보다 두 배 느립니다. 내 테스트에서 나는 71M Exp를 계산하지만 "유일한"1.8M의 다른 주장을 가지고 있습니다. –

5

C에서 루프의 호출 Math.exp()을 랩핑하고 싶을 것입니다. 그렇지 않으면 Java와 C 사이의 마샬링 (marshalling) 오버 헤드가 성능상의 이점을 압도하게됩니다.

0

Java 코드가 JIT (Just-In-Time) 컴파일러로 원시 코드로 컴파일되므로 JNI를 사용하여 원시 코드를 호출 할 이유가 없습니다.

또한 입력 매개 변수가 부동 소수점 실수 인 메서드의 결과를 캐시하지 않아야합니다. 시간에 따라 얻은 이익은 사용 된 공간의 양에서 매우 손실됩니다.

0

JNI를 사용할 때의 문제점은 JNI를 호출 할 때 발생하는 오버 헤드입니다. Java 가상 머신은 요즘 꽤 최적화되어 있으며 내장 된 Math.exp()에 대한 호출이 자동으로 C exp() 함수를 호출하도록 자동으로 최적화되며 심지어는 똑 바른 x87 부동 소수점 어셈블리로 최적화 될 수도 있습니다 명령.

2

진짜 질문은 이것이 너에게 병목이 되었습니까? 응용 프로그램을 프로파일 링하여 속도 저하의 주요 원인이라고 생각하십니까?

그렇지 않은 경우 Java 버전을 사용하는 것이 좋습니다. 사전 최적화를 시도하지 마십시오. 개발 속도가 느려지므로 미리 최적화하십시오. 문제가되지 않을 수도있는 시간에 많은 시간을 할애 할 수 있습니다.

그 말은 당신의 테스트가 당신에게 답을 준 것 같아요. jni + C가 느린 경우 java의 버전을 사용하십시오.

3

일괄 적으로 실행하면 더 빠르게 실행할 수 있습니다. JNI 호출을하면 오버 헤드가 추가되므로 계산할 필요가있는 각 exp()에 대해 JNI 호출을 수행하고 싶지는 않습니다. 100 개의 값 배열을 전달하고 결과가 성능에 도움이되는지 확인하려고합니다.

0

필요에 맞게 직접 작성하십시오.

예를 들어 모든 지수가 2의 제곱이면 비트 이동을 사용할 수 있습니다. 제한된 범위 또는 값 세트로 작업하는 경우 조회 표를 사용할 수 있습니다. 핀 포인트 정밀도가 필요하지 않은 경우 부정확하지만 빠른 알고리즘을 사용합니다.

0

JNI 경계를 통한 호출과 관련된 비용이 있습니다.

네이티브 코드에 exp()를 호출하는 루프를 옮겨서 하나의 네이티브 호출이 가능하면 더 나은 결과를 얻을 수 있지만 순수 자바보다 훨씬 빠르다. 해결책.

응용 프로그램에 대한 세부적인 내용은 알 수 없지만 호출에 대한 가능한 인수 집합이 상당히 적 으면 미리 계산 된 조회 테이블을 사용하여 Java 코드를 더 빠르게 만들 수 있습니다.

0

달성하려는 목표에 따라 빠른 알고리즘이 있습니다. 문제 공간이 특정 범위로 제한되어 있습니까? 특정 해상도, 정밀도 또는 정확도 만 필요합니까?

문제를 잘 정의하면 보간이 포함 된 테이블을 사용할 수 있습니다. 예를 들어, 거의 모든 다른 알고리즘을 물에서 날려 버릴 수 있습니다.

성능 상쇄를 위해 exp에 적용 할 수있는 제약 조건은 무엇입니까?

-adam

0

피팅 알고리즘을 실행하고 피팅 결과의 최소 오류가 Math.exp()의 정밀도보다 큰 입니다.

초월 함수는 항상 더하기 또는 곱셈과 잘 알려진 병목보다 훨씬 느립니다. 값이 좁은 범위에 있음을 알고 있다면 조회 테이블 (두 개의 정렬 된 배열, 하나의 입력, 하나의 출력)을 간단하게 작성할 수 있습니다. Arrays.binarySearch를 사용하여 올바른 색인을 찾고 [index] 및 [index + 1]의 요소로 값을 보간하십시오.

또 다른 방법은 번호를 분할하는 것입니다. 예 : 3.81로 나누고 3 + 0.81로 나눕니다. 이제 e = 2.718을 세 번 곱하면 20.08이됩니다.

지금 0.81. 0과 1 사이의 모든 값은 잘 알려진 지수 시리즈

1 + X + X^2/2 + X^6분의 3 + X^등 24분의 4 ....

으로 빠른 수렴 정밀도를 위해 필요한만큼의 용어를 사용하십시오. 불행히도 x가 1에 가까워지면 속도가 느려집니다. x^4에 가면 올바른 것이 아니라 2.2445가됩니다. 2.2448

2.781^3 = 45.07 2 천분의 1 부분 오류 (올바른 : 45.15).

15

이것은 이미 여러 번 요청되었습니다 (예 : here 참조). 여기 this blog posting 복사 Math.exp()에 대한 근사치이다

public static double exp(double val) { 
    final long tmp = (long) (1512775 * val + (1072693248 - 60801)); 
    return Double.longBitsToDouble(tmp << 32); 
} 

그것은 기본적 2,048 항목 및 항목들 사이의 선형 보간과 룩업 테이블과 동일하다,하지만 모든 IEEE 부동 소수점 트릭. 수학보다 5 배 빠릅니다.exp() 내 컴퓨터에 있지만 -server 함께 컴파일 할 경우이 다를 수 있습니다.

0

OpenJDK의 최신 릴리스 (here 참조)에서 더 이상 관련이 없지만 Math.exp를 내장으로 사용해야합니다 (모르는 경우 확인하십시오. here).

이렇게하면 Hotspot VM이 Math.exp에 대한 호출을 런타임에 프로세서의 특정 구현으로 대체하기 때문에 대부분의 아키텍처에서 성능을 떨어 뜨릴 수 있습니다. 이 호출은 아키텍처에 맞게 최적화되어 있기 때문에 절대로 이길 수 없습니다.

1

Commons Math3은 최적화 된 버전 (FastMath.exp(double x))과 함께 제공됩니다. 그것은 내 코드의 속도를 상당히 높였다.

Fabien

일부 테스트를 실행하고 Math.exp() 거의 배 빠른 것을 알았다 :

가 EXP (x)는 함수의 결과는 거의 원형 인 계산 : 여기서

0.75s for Math.exp  sum=1.7182816693332244E7 
0.40s for FastMath.exp sum=1.7182816693332244E7 

는 자바 독이다. 입력 값의 99.9 %에 대한 이론 값으로 정확하게 반올림됩니다. 그렇지 않으면 1 UPL 오류가 발생합니다.

방법 :

Lookup intVal = exp(int(x)) 
    Lookup fracVal = exp(int(x-int(x)/1024.0) * 1024.0); 
    Compute z as the exponential of the remaining bits by a polynomial minus one 
    exp(x) = intVal * fracVal * (1 + z) 

정확도 계산 정밀도를 63 비트로 완료되기 때문에, 그 결과가 정확하게 그렇지 미만 ULP 오차와 입력 값의 99.9 %로 반올림한다.

+0

나에게 가장 좋은 해결책! –