如果与有条件的速度比较

时间:2014-07-30 06:05:11

标签: java if-statement benchmarking conditional-operator microbenchmark

我有一个想法,我会使用条件运算符将我的一些if块转换为单行。但是,我想知道是否存在速度差异。我运行了以下测试:

static long startTime;
static long elapsedTime;
static String s;

public static void main(String[] args) {
    startTime = System.nanoTime();
    s = "";
    for (int i= 0; i < 1000000000; i++) {
        if (s.equals("")) {
            s = "";
        }
    }

    elapsedTime = System.nanoTime() - startTime;

    System.out.println("Type 1 took this long: " + elapsedTime + " ns");

    startTime = System.nanoTime();
    s = "";
    for (int i= 0; i < 1000000000; i++) {
        s = (s.equals("") ? "" : s);
    }

    elapsedTime = System.nanoTime() - startTime;

    System.out.println("Type 2 took this long: " + elapsedTime + " ns");
}

这是我的结果:

  

Type 1花了这么长时间:3293937157 ns

     

类型2花了这么长时间:2856769127 ns

我在这里做错了吗?

假设s.equals("")必然是正确的,这是一种使代码更快的可行方法吗?

3 个答案:

答案 0 :(得分:6)

, is this a viable way to make your code faster?

如果String s;是非静态字段,您甚至可以加快速度。当referencing十亿次

时,静态字段比非静态字段慢
public static void main(String[] args) {

    startTime = System.nanoTime();
    String s = "";
    .
    .
}

修改

为什么它更快?

这是由于将字符串引用到静态字段。

你可以在它的字节代码中看到它

    0: ldc           #23                 // String
       2: putstatic     #25                 // Field s:Ljava/lang/String;
       5: iconst_0
       6: istore_1
       7: goto          22
      10: getstatic     #25                 // Field s:Ljava/lang/String;
      13: ldc           #23                 // String
      15: invokevirtual #27                 // Method java/lang/String.equals:(L
java/lang/Object;)Z
      18: pop
      19: iinc          1, 1
      22: iload_1
      23: ldc           #33                 // int 1000000000
      25: if_icmplt     10
      28: return

正如您所看到的,getStaticputStatic将被称为十亿次,它的作用是它将调用静态字段的引用并使用putStatic放置字符串的引用

getStatic - 获取类的静态字段值,其中字段由常量池索引中的字段引用标识(index1&lt;&lt;&lt; 8&index2)

putStatic - 将静态字段设置为类中的值,其中字段由常量池中的字段引用索引标识(indexbyte1&lt;&lt;&lt;&lt; 8&indexbyte2)

查看导致程序缓慢的位移

此外,如果您使用的是global/member field,它会创建相同的字节码,但它会使用 getfieldputfield与静态getStaticputStatic

相同

现在让我们看看non static field字节码

      0: ldc           #21                 // String
       2: astore_1
       3: iconst_0
       4: istore_2
       5: goto          23
       8: aload_1
       9: ldc           #21                 // String
      11: invokevirtual #23                 // Method java/lang/String.equals:(L
java/lang/Object;)Z
      14: ifeq          20
      17: ldc           #21                 // String
      19: astore_1
      20: iinc          2, 1
      23: iload_2
      24: ldc           #29                 // int 1000000000
      26: if_icmplt     8
      29: return

正如您所看到的,它只使用astore_1aload_1来保存和加载非静态字段的引用而无需额外的操作。

答案 1 :(得分:5)

这听起来像是对我的过早优化。如果您仍打算以这种方式对这两种实现进行微基准测试,我建议使用isEmpty(),因为与equals()相比,它的底层代码更简单。通过这种方式,我的意思是编译器/ JVM将为您做的任何优化都不太可能由equals()中发生的事情触发,更能反映一个实现相对于另一个实现的任何微小优势,假设真的很重要

可读性应该是您决定是否要使用if-else? :的更好规则。

答案 2 :(得分:2)

其他答案包含相关的有用信息,但如果第一个表单比第二个表单更有效,它们都不会解决真正的问题。

这种基准测试不能提供可靠的结果,因为它没有正确完成:在Java代码基准测试中,一个重要的“经验法则”是提供预热。在这种情况下,第一个循环为第二个循环提供预热。

This answer提供了有关微基准测试以及一些有用链接的其他说明。