2012-01-19 13 views
4

私は繰り返し繰り返しの数が多いと非常にタイトなループ内で、次の式を評価するJavaメソッドを持っています。Math.abs(a-b) - Math.abs(c-d)の実装は高速ですか?

Math.abs(a - b) - Math.abs(c - d) 

abcdは彼らの全体の範囲に及ぶことができlong値であり、タイプ。それらは各ループ反復で異なり、それらは私が知っている不変量を満たさない。

プロファイラは、プロセッサ時間のかなりの部分がこのメソッドで費やされたことを示します。私はが最初に最適化の他の手段を追求している間、上記の表現を計算するよりスマートな方法があるかどうか疑問に思っていました。

Math.abs()の呼び出しを手動で実行すると、非常にわずかな(あれば)パフォーマンスが向上しますが、この式の評価を高速化するために使用できる数学的なトリックはありますか?

+0

値がその型の範囲全体に及ぶ場合、たとえばaが非常に大きな負の数であり、bが次のような場合にこのコードでオーバーフローが発生します。非常に大きな正の数(またはその逆)。アルゴリズムを確認してください。 –

+0

@JBNizet:私はまだオーバーフローに遭遇していないので、ある種の不変であると思います。 「全範囲に渡る」条件は、ほとんどが、例えば、すべての変数は32ビットで収まります。 – thkala

+0

このようなオーバーフローは例外を発生させないことに注意してください。したがって、彼らは気づかれなかったかもしれません。 –

答えて

3

::私はそれがこのようなものかもしれない展開権利を得た場合

public static long diff(final long a, final long b, final long c, final long d) { 
    final long a0 = (a < b)?(b - a):(a - b); 
    final long a1 = (c < d)?(d - c):(c - d); 

    return a0 - a1; 
} 

を私は測定可能な性能向上を経験した - およそ10-15アプリケーション全体の%。私は一度、このメソッドを呼び出して、むしろ2倍Math.abs()を呼び出す:

  • メソッド呼び出しの除去:私はこれが原因のほとんどであると考えています。確かに、静的メソッド呼び出しはあまりにも高価ではありませんが、それでも影響はあります。

  • 否定操作のカップルの排除:これは、コードのわずかに増加大きさによって相殺されるかもしれないが、私は喜んでそれが実際に違いを作ったことを信じるように自分自身をだますでしょう。

EDIT:

実際に周りの他の方法だと思われます。コードを明示的にインライン展開することは、私のマイクロベンチマークのパフォーマンスには影響していないようです。絶対値の計算方法を変更すると...

+1

最初のポイントとにかく、JITはそのような簡単な呼び出しをインラインにします。あなたのロジックを関数に入れて呼び出すことで、時間的にも違うことも簡単にテストできます。 2番目の点は有効ですが、マイクロベンチマークでは約10%のスピードアップが得られます(コードを投稿できますが、OSR、ウォームアップなどを見ています)。 – Voo

+0

@Voo:true、私のベンチマークに*問題があります*カウントしないカウンタを使用して、結果を歪めます。 – thkala

4

私はプロファイラがあなたにそのような些細な「メソッド」をプロファイルして(そして頭を付け加えようとする)本当の結果を与えていないと思われます。プロファイルがなければ、Math.absは少数のマシンコード命令に変換することができ、それ以上の高速化はできません。

これを確認するためにマイクロベンチマークを行うことをお勧めします。私は、データをより高価なオーダーにすることを期待しています。

long a = 10, b = 6, c = -2, d = 3; 

int runs = 1000 * 1000 * 1000; 
long start = System.nanoTime(); 
for (int i = 0; i < runs; i += 2) { 
    long r = Math.abs(i - a) - Math.abs(c - i); 
    long r2 = Math.abs(i - b) - Math.abs(d - i); 
    if (r + r2 < Integer.MIN_VALUE) throw new AssertionError(); 
} 
long time = System.nanoTime() - start; 
System.out.printf("Took an average of %.1f ns per abs-abs. %n", (double) time/runs); 

プリント

Took an average of 0.9 ns per abs-abs. 
+0

確かに可能ですが、私はループが指標になるほど十分に緊密だとは思いますが。ところで、x86_64に64ビット整数の命令があることは確かですか? – thkala

+0

ええと、それは 'movabs'を持っていますが、それは私が思ったことをするようには見えません。 –

+0

これは実際にインタプリタのパフォーマンスをテストしています(JIT自体は、ループを最適化できると認識します。このテストを何度か実行すると、私にとっては '178、176、0、0、0、.. 'が返されます) – Voo

0

その方法自体が問題になるあなたは確かにありますか?おそらく、このメソッドの呼び出しは膨大で、プロファイラーに集計された結果(TIME_OF_METHOD_EXECUTION X NUMBER_OF_INVOCATIONSなど)が表示されるだけでしょうか?

1

いつでも関数を展開して最適化を試みることができます。キャッシュミスが増えない場合は、高速になる可能性があります。私はこの小さな方法で使用して終了し

if(a<b) 
{ 
    if(c<d) 
    { 
     r=b-a-d+c; 
    } 
    else 
    { 
     r=b-a+d-c; 
    } 
} 
else 
{ 
    if(c<d) 
    { 
     r=a-b-d+c; 
    } 
    else 
    { 
     r=a-b+d-c; 
    } 
} 
+0

+1このコードをインライン展開すると助けになりました... – thkala

関連する問題