使用 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
底线:这两种方法的执行情况与预期的一样(执行时间的差异小于测量误差)。
我想测试这两种方法中哪一种执行得更快,但是取决于我首先 运行 哪一种方法,该方法似乎总是 运行 较慢。我怀疑 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
底线:这两种方法的执行情况与预期的一样(执行时间的差异小于测量误差)。