1

私は何度も実行され、非常に効率的でなければならないパスファインディングアルゴリズムを持っているので、パフォーマンスを向上させるために何ができるのか疑問に思っています。私はこれを言うifステートメントを持っています:

if (!(n != 1 && map.isCornerObstructed(p)) {
    // Do stuff...
}

二重反転は、この論理的に同等のバージョンよりもわずかに長くかかるように思われます。

if (n == 1 || !map.isCornerObstructed(p)) {
    // Do stuff...
}

問題は、前者の方がコードのコンテキストで読みやすいことです。そのため、結果がどうなるかわからない場合は、前者を変更するのはやや気が進まないのです。

一方はもう一方よりも効率的ですか?それとも、Javaコンパイラはこのようなものを自動的に最適化するのに十分賢いですか?

4

5 に答える 5

4

これらのマイクロ最適化ではなく、アルゴリズムの複雑さの最適化に焦点を合わせてください。

この場合、1つの比較(!=および==)、1つの論理演算子、1つの否定が表示されます。n!= 1の場合、論理演算の数は同じです。n == 1の場合、最初のケースの論理演算の数は2番目のケースよりも多くなります。n!= 1の場合がさらにある場合は、おそらく問題ではありません。n == 1の場合が多い場合は、2番目の場合の方がわずかに速い可能性があります。しかし、JITコンパイラ全体が背後で機能しているため、実際に何が起こっているのかわかりません。マイクロ最適化はC/C ++コードで機能する可能性がありますが、Javaへの影響は本当に疑わしいです。

于 2012-05-30T05:35:57.787 に答える
4

コード

Set<String> set = new HashSet<String>();
int n = set.size();
Object o = new Object();
if (!(n != 1 && set.contains(o))) {
  System.out.println("Foo");
  // Do stuff...
}

バイトコードを生成します

 0  new java.util.HashSet [16]
 3  dup
 4  invokespecial java.util.HashSet() [18]
 7  astore_1 [set]
 8  aload_1 [set]
 9  invokeinterface java.util.Set.size() : int [19] [nargs: 1]
14  istore_2 [n]
15  new java.lang.Object [3]
18  dup
19  invokespecial java.lang.Object() [8]
22  astore_3 [o]
23  iload_2 [n]
24  iconst_1
25  if_icmpeq 38
28  aload_1 [set]
29  aload_3 [o]
30  invokeinterface java.util.Set.contains(java.lang.Object) : boolean [25] [nargs: 2]
35  ifne 46
38  getstatic java.lang.System.out : java.io.PrintStream [29]
41  ldc <String "Foo"> [35]
43  invokevirtual java.io.PrintStream.println(java.lang.String) : void [37]
46  return

コード

Set<String> set = new HashSet<String>();
int n = set.size();
Object o = new Object();
if (n == 1 || !set.contains(o)) {
  System.out.println("Foo");
  // Do stuff...
}

バイトコードを生成します

 0  new java.util.HashSet [16]
 3  dup
 4  invokespecial java.util.HashSet() [18]
 7  astore_1 [set]
 8  aload_1 [set]
 9  invokeinterface java.util.Set.size() : int [19] [nargs: 1]
14  istore_2 [n]
15  new java.lang.Object [3]
18  dup
19  invokespecial java.lang.Object() [8]
22  astore_3 [o]
23  iload_2 [n]
24  iconst_1
25  if_icmpeq 38
28  aload_1 [set]
29  aload_3 [o]
30  invokeinterface java.util.Set.contains(java.lang.Object) : boolean [25] [nargs: 2]
35  ifne 46
38  getstatic java.lang.System.out : java.io.PrintStream [29]
41  ldc <String "Foo"> [35]
43  invokevirtual java.io.PrintStream.println(java.lang.String) : void [37]
46  return

これはまったく同じです。したがって、どんなに細かく測定しても性能に差はありません。コンパイルされたコードは同じです。

(この理由は、実際にステートメントを個別の条件テストと分岐にjavac分解するため、実際に各可能性の経路を決定することに注意してください。)if

于 2012-05-30T06:36:09.723 に答える
1

ここでの私の懸念は、何よりもmap.isCornerObstructed(p)呼び出しであり、ブール論理は高速ですが、メソッド呼び出しはそれほど多くありません。

私は2番目のオプションを選びます-それは最も読みやすく、最も効率的であるように思われます。

コメントで指摘されているように、&&と||の両方 短絡するため、最も簡単なテスト方法が必要です。ラップされた反転を使用することは、実行する必要のあるもう1つのレイヤーです。

于 2012-05-30T05:38:40.003 に答える
0

あなたがあちこちでその声明をするなら、誰が気にします。どちらも最大で数cpuサイクルになります。

于 2012-05-30T05:36:48.533 に答える
0

2つ目は、より最適化できます。ただし、コードのこの部分が実行される回数によって異なりますが、

より具体的には、2番目のオプションで:

if (n == 1 || !map.isCornerObstructed(p)) {
// Do stuff...
}

n == 1がtrueの場合、javaは!map.isCornerObstructed(p)を実行せず、n == 1は!map.isCornerObstructed(p)よりも最適化されているため、「if」ステートメントをある方法で記述する必要があります。ほとんどの場合、最初のより単純なステートメントが答えを示しています。

javaが最適化するかどうか疑問がある場合は(!(n!= 1 && map.isCornerObstructed(p))、テストケースを記述し、各ステートメントをループで1000000回実行し、System.currentmilsを使用して各メソッドの時間コストを計算すると、そこに行きます

于 2012-05-30T06:05:09.910 に答える