2008-09-15 10 views
10

私は非常に頻繁にMath.exp()をjavaから計算する必要があります。より速く実行するためにネイティブバージョンを得ることは可能ですかjavaMath.exp() ??JNI経由のMath.exp()が高速ですか?

私はちょうどjni + Cを試しましたが、それは単なる普通よりも遅いですjava

+0

Math.exp()とJNIバージョンの正確な時間を取得するためのパフォーマンステストを行いましたか? JITの効果を見るために10k回呼ばれた後はどうですか? – martinatime

+0

これはあなたのJVMに依存しますが、通常は 'Math.exp' *がC *で実装されています。あなたはより高速な(精度の低い)アルゴリズムを使用したいかもしれません。 – Joni

答えて

11

+1独自のexp()実装を記述しています。つまり、これがの場合、実際にボトルネックをアプリケーションに適用します。あなたが少しの不正確さに対処することができれば、非常に効率的な指数推定アルゴリズムが数多くありますが、その中には何世紀にもわたるものがあります。私が理解しているように、Javaのexp()実装は "正確な"結果を返さなければならないアルゴリズムであってもかなり遅いです。

ああ、純粋なJavaでそのexp()実装を書くことを恐れないでください。 JNIには多くのオーバーヘッドがあり、JVMは実行時にC/C++が達成できるものを超えてバイトコードを最適化することができます。

+0

ここで2つの重要な点:(1)JNIオーバーヘッドは他のすべての考慮事項を上回ることがあります。 (2)JVM JITは、マシンが十分に温められている限り、小さなメソッドを最適化すると、驚くほど良い(時にはC/C++よりも速い)。 – kevinarpe

6

Javaを使用してください。

また、expの結果をキャッシュしておけば、それらを再度計算するよりも高速に検索することができます。

+0

どのように結果をキャッシュしますか?キャッシュにはかなりのコストがかかる可能性があります.HashMapを試してみましたが、単にexpを計算するよりも2倍遅かったです。私のテストでは、71Mを計算しますが、「唯一の」1.8Mの異なる引数を使用します。 –

5

あなたはCの中でループMath.exp()を呼んでも何とかしたいと思います。そうしないと、JavaとCのマーシャリングによるオーバーヘッドがパフォーマンス上の利点を圧倒します。

0

ジャストインタイム(JIT)コンパイラでJavaコードがネイティブコードにコンパイルされるため、JNIを使​​用してネイティブコードを呼び出す理由はありません。

また、入力パラメータが浮動小数点実数であるメソッドの結果をキャッシュしないでください。時間内に得られた利益は、使用されるスペースの量が非常に失われます。

0

JNIを使​​用する際の問題は、JNIを呼び出す際のオーバーヘッドです。 Java仮想マシンは近年かなり最適化されており、組み込みのMath.exp()への呼び出しは自動的にC exp()関数に直接コールするように最適化され、ストレートx87浮動小数点アセンブリに最適化されることさえあります指示。

2

本当の質問は、これはあなたのためのボトルネックになっていますか?あなたのアプリケーションをプロファイリングしましたが、これがスローダウンの大きな原因であることがわかりましたか?

そうでなければ、Javaのバージョンを使用することをお勧めします。事前に最適化しないと、開発が遅くなるだけです。あなたは問題ではないかもしれない問題に長い時間を費やすかもしれません。

あなたのテストはあなたに答えを与えたと言われています。 jni + Cが遅い場合は、javaのバージョンを使用してください。 JNIを使​​用することに伴うオーバーヘッドが単にあります

3

バッチで実行すると、実行速度が向上する場合があります。 JNI呼び出しを作成するとオーバーヘッドが増えるため、計算する必要のあるexp()ごとにそれを実行したくありません。私は100の値の配列を渡し、結果がパフォーマンスに役立つかどうかを調べることを試みます。

0

お客様のニーズに合わせて独自に作成してください。

たとえば、すべての指数が2の累乗である場合、ビットシフトを使用できます。限られた範囲または値のセットで作業する場合は、ルックアップテーブルを使用できます。ピンポイント精度が必要ない場合は、不正確ですが高速なアルゴリズムを使用します。

0

JNI境界をまたいだ呼び出しに関連するコストがあります。

ネイティブコードにexp()を呼び出すループを移動して、ネイティブコードが1つだけであれば、より良い結果が得られるかもしれませんが、純粋なJava溶液。

あなたのアプリケーションの詳細はわかりませんが、コールの引数がかなり限られている場合は、あらかじめ計算されたルックアップテーブルを使用してJavaコードを高速化できます。

0

あなたが達成しようとしているものによっては、より高速なアルゴリズムがあります。問題のスペースは一定の範囲に制限されていますか?特定の解像度、精度、精度などが必要ですか?

問題をきちんと定義すると、補間付きのテーブルを使用できますこのインスタンスは、水中から他のアルゴリズムをほとんど吹き飛ばすことになります。

パフォーマンスのトレードオフを得るためにexpに適用できる制約はありますか?

-adam

0

私はフィッティングアルゴリズムを実行し、フィッティング結果の最小誤差はMath.exp()の精度よりも大きくなります。

超越関数は、加算または乗算とよく知られているボトルネックよりもずっと遅いです。値が狭い範囲にあることがわかっている場合は、ルックアップテーブル(並べ替えられた2つの配列、入力1つ、出力1つ)を作成するだけです。 Arrays.binarySearchを使用して、[index]と[index + 1]の要素で正しいインデックスと補間値を見つけます。

もう1つの方法は、番号を分割することです。例えば3.81で分割し、3 + 0.81で分割します。 e = 2.718を3回乗算して20.08を得ます。

0.81になりました。 0と1の間の全ての値は、周知の指数級数

1 + X + X^2/2 + X^3/6 + X ^等4月24日....

と高速収束します正確さのために必要な期間を取る。残念ながら、xが1に近づくと遅くなります。x^4に行くと、正しいのではなく2.2445が得られます。2.2448

結果2.781^3 = 20.08に2.781^0.81 = 2.2445を掛けます。 45.07 2千の部分の誤り(正しい: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); 
} 

それは基本的に2048個のエントリとエントリとの間の線形補間とルックアップテーブルと同じであるが、しかし、すべてこのIEEE浮動小数点のトリック有します。数学は数学よりも5倍高速です。私のマシン上でexp()を実行しますが、-serverを使ってコンパイルすると大幅に変化する可能性があります。

0

OpenJDKの最新リリース(hereを参照)では、Math.expを組み込みにする必要があります(それがわからない場合は、チェックしてくださいhere)。

これは、Hotspot VMがMath.expへの呼び出しを実行時にexpのプロセッサ固有の実装に置き換えることを意味するため、ほとんどのアーキテクチャでパフォーマンスを最適化できます。これらの呼び出しはアーキテクチャに合わせて最適化されているので、決してこれらの呼び出しを打ち負かすことはできません。

1

Commons Math3は、最適化バージョン:FastMath.exp(double x)に同梱されています。それは私のコードを大幅にスピードアップしました。

Fabienは、いくつかのテストを実行し、それがほぼ2倍に高速Math.exp()ようだったことが判明:ここ

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

はjavadocのです:

EXPを計算します(x)は、関数の結果はほぼ丸いです。入力値の99.9%の理論値に正しく丸められます。そうでない場合は、UPLエラーが1つ発生します。

方法:

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ビットで行われ、その結果が正しくそうでなければ1つの未満ULPエラーで、入力値の99.9%のために丸くされるべきです。

+0

私にとって最高のソリューション! –

関連する問題