使用 Java/Eclipse 测试代码执行速度

Testing speed of code execution with Java/Eclipse

我想测试这两种方法中哪一种执行得更快,但是取决于我首先 运行 哪一种方法,该方法似乎总是 运行 较慢。我怀疑 Eclipse 正在使用某种缓存机制来加速第二种方法的执行。

private static int method1(int n) {
    int result = 0;
    for (int i = 0; i < n + 1; ++i) {
        result += i;
    }
    return result;
}

private static int method2(int n) {
    int result = 0;
    int i = 0;
    while (i < n + 1) {
        result += i++;
    }
    return result;
}

这是我用来测试时间差异的主要功能。

long start, end;

start = new Date().getTime();
for (int i = 0; i < 100000; ++i) {
    method1(i);
}
end = new Date().getTime();
System.out.println(end - start); // 47

start = new Date().getTime();
for (int i = 0; i < 100000; ++i) {
    method2(i);
}
end = new Date().getTime();
System.out.println(end - start); // 32

While 和 For 循环具有相似的性能。

也许这个相似的 post 会对您有所帮助 Java For Loop Vs While Loop Performance Difference

使用new Date().getTime()给出挂钟时间。但这确实不是我们作为开发人员想要的(大多数时候,直到进行一些使用企业级基准测试的基准测试)因为挂钟时间受到许多后台进程的影响,所以为了应对 Java 提供更复杂的 API 来测量时间。

要排除其他系统 activity 的影响,您需要测量应用程序 "User time"。

  • 用户时间”是运行您的应用程序自己的代码花费的时间。
  • "CPU时间"是用户时间加上系统时间。这是您的应用程序使用 CPU 的总时间。

下面的示例演示了 CPU 和使用 ManagementFactory.getThreadMXBean() API 计算用户时间。

    Thread thread = new Thread(){
            public void run() {
                for (int i = 0; i < 100000; ++i) {
                    int result = 0;
                    for (int j = 0; j < i + 1; ++j) {
                        result += j;
                    }
                }
                System.out.println("FOR approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
                System.out.println("FOR approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
            };
        };
        thread.start();

        Thread thread2 = new Thread(){
            public void run() {
                for (int i = 0; i < 100000; ++i) {
                    int result = 0;
                    int j = 0;
                    while (j < i + 1) {
                        result += j++;
                    }
                }
                System.out.println("WHILE approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
                System.out.println("WHILE approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
            };
        };
        thread2.start();


话虽如此,我真的不确定你为什么会出现意外行为,我 运行 你在 Eclipse 和 IntelliJ 中的代码 IDE,而且我总是得到比 WHILE 更快的 FOR 循环方法环形。

可能尝试重新启动 Eclipse 并减少后台进程的数量 运行 或者不要 运行 Eclipse 但 运行 来自 [= 的测试78=]命令行,以便您可以确定结果

从下面的字节码分析可以看出,WHILE和FOR循环方法生成了相同的字节码,这意味着会有相同的assemble代码,因此相同的时间CPU将执行指令

但实际上,当我们在您的 IDE 或其他方面 运行 时,会受到后台进程的影响,因此会观察到不同的时间。 但在这种特殊情况下 - WHILE v/s FOR,更适合进行字节码分析并得出结论,WHILE 和 FOR 循环方法将花费相同的时间。


FOR循环字节码:

{
  public Test2();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return
      LineNumberTable:
        line 3: 0
        line 4: 8
        line 3: 15
        line 6: 21
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 2
        locals = [ int ]
           frame_type = 250 /* chop */
          offset_delta = 18

}

WHILE循环的字节码:

{
  public Test();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: bipush        10
         5: if_icmpge     21
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_1
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: iinc          1, 1
        18: goto          2
        21: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 8
        line 6: 15
        line 8: 21
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 2
        locals = [ int ]
           frame_type = 18 /* same */

}

延伸阅读:

使用 jmh,我得到以下结果(对于不同的 n 值),每个方法调用的分数以纳秒为单位(更小 = 更好):

Benchmark                          (n)  Mode  Samples          Score         Error  Units
c.a.p.SO31495089.method1             1  avgt       10          2.149        0.027  ns/op
c.a.p.SO31495089.method1        100000  avgt       10      34626.763      441.915  ns/op
c.a.p.SO31495089.method1    1000000000  avgt       10  322506247.405  6774340.047  ns/op
c.a.p.SO31495089.method2             1  avgt       10          2.159        0.028  ns/op
c.a.p.SO31495089.method2        100000  avgt       10      34581.273      571.416  ns/op
c.a.p.SO31495089.method2    1000000000  avgt       10  320011679.005  4049907.844  ns/op

第二个运行:

Benchmark                          (n)  Mode  Samples          Score         Error  Units
c.a.p.SO31495089.method1             1  avgt       10          2.164        0.029  ns/op
c.a.p.SO31495089.method1        100000  avgt       10      34706.194      365.189  ns/op
c.a.p.SO31495089.method1    1000000000  avgt       10  320269697.300  1696038.683  ns/op
c.a.p.SO31495089.method2             1  avgt       10          2.160        0.040  ns/op
c.a.p.SO31495089.method2        100000  avgt       10      34627.163      325.981  ns/op
c.a.p.SO31495089.method2    1000000000  avgt       10  320698252.840  2332275.430  ns/op

底线:这两种方法的执行情况与预期的一样(执行时间的差异小于测量误差)。