1

我有一个运行多次并且必须非常高效的寻路算法,所以我想知道我可以做些什么来提高性能。我有一个 if 语句,它说:

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

在我看来,双重反转会比这个逻辑上等价的版本稍微长一点:

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

问题是前者在代码上下文中更具可读性,所以如果我不知道结果会是什么,我有点不愿意改变它。

一个比另一个更有效吗?或者 Java 编译器是否足够聪明,可以自动优化这样的事情?

4

5 回答 5

4

请专注于优化算法的复杂性,而不是这些微优化。

对于这种情况,我看到一个比较 ( !=and ==)、一个逻辑运算符、一个否定。在 n != 1 的情况下,逻辑运算的数量是相同的。在 n == 1 的情况下,第一种情况的逻辑运算次数多于第二种情况。如果有更多 n != 1 案例,那么可能没关系。如果有更多 n == 1 案例,那么第二种情况可能会更快。但是随着整个 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) 调用,布尔逻辑很快,但方法调用不多。

我会选择第二种选择——这似乎是最容易阅读和最有效的。

正如评论所指出的, && 和 || 短路,因此您需要最简单的测试方法,使用包装的反转只是需要执行的另一层。

于 2012-05-30T05:38:40.003 回答
0

第二个可以更优化。但这取决于执行多少次,这部分代码,

更具体地说,在您的第二个选项中:

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 回答
0

如果你在这里和那里做这个声明,谁在乎。任何一个都最多相当于几个 cpu 周期。

于 2012-05-30T05:36:48.533 回答