我使用以下代码来测试try块的速度有多慢。令我惊讶的是,try块使它更快。为什么呢?
public class Test {
int value;
public int getValue() {
return value;
}
public void reset() {
value = 0;
}
// Calculates without exception
public void method1(int i) {
value = ((value + i) / i) << 1;
// Will never be true
if ((i & 0xFFFFFFF) == 1000000000) {
System.out.println("You'll never see this!");
}
}
public static void main(String[] args) {
int i;
long l;
Test t = new Test();
l = System.currentTimeMillis();
t.reset();
for (i = 1; i < 100000000; i++) {
t.method1(i);
}
l = System.currentTimeMillis() - l;
System.out.println("method1 took " + l + " ms, result was "
+ t.getValue());
// using a try block
l = System.currentTimeMillis();
t.reset();
for (i = 1; i < 100000000; i++) {
try {
t.method1(i);
} catch (Exception e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("method1 with try block took " + l + " ms, result was "
+ t.getValue());
}
}
我的机器正在运行64位Windows 7和64位JDK7。我得到了以下结果:
method1 took 914 ms, result was 2
method1 with try block took 789 ms, result was 2
我已经多次运行代码,每次得到几乎相同的结果。
更新
以下是在MacBook Pro,Java 6上运行测试十次的结果.Tre-catch使得该方法更快,与在Windows上相同。
method1 took 895 ms, result was 2
method1 with try block took 783 ms, result was 2
--------------------------------------------------
method1 took 943 ms, result was 2
method1 with try block took 803 ms, result was 2
--------------------------------------------------
method1 took 867 ms, result was 2
method1 with try block took 745 ms, result was 2
--------------------------------------------------
method1 took 856 ms, result was 2
method1 with try block took 744 ms, result was 2
--------------------------------------------------
method1 took 862 ms, result was 2
method1 with try block took 744 ms, result was 2
--------------------------------------------------
method1 took 859 ms, result was 2
method1 with try block took 765 ms, result was 2
--------------------------------------------------
method1 took 937 ms, result was 2
method1 with try block took 767 ms, result was 2
--------------------------------------------------
method1 took 861 ms, result was 2
method1 with try block took 744 ms, result was 2
--------------------------------------------------
method1 took 858 ms, result was 2
method1 with try block took 744 ms, result was 2
--------------------------------------------------
method1 took 858 ms, result was 2
method1 with try block took 749 ms, result was 2
答案 0 :(得分:19)
如果在同一方法中有多个长时间运行的循环,则可以在第二个循环上触发整个方法的优化,结果不可预测。避免这种情况的一种方法是:
您会看到一些变化,有时结果不确定。即差异高于差异。
public class Test {
int value;
public int getValue() {
return value;
}
public void reset() {
value = 0;
}
// Calculates without exception
public void method1(int i) {
value = ((value + i) / i) << 1;
// Will never be true
if ((i & 0xFFFFFFF) == 1000000000) {
System.out.println("You'll never see this!");
}
}
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i < 5; i++) {
testWithTryCatch(t);
testWithoutTryCatch(t);
}
}
private static void testWithoutTryCatch(Test t) {
t.reset();
long l = System.currentTimeMillis();
for (int j = 0; j < 10; j++)
for (int i = 1; i <= 100000000; i++)
t.method1(i);
l = System.currentTimeMillis() - l;
System.out.println("without try/catch method1 took " + l + " ms, result was " + t.getValue());
}
private static void testWithTryCatch(Test t) {
t.reset();
long l = System.currentTimeMillis();
for (int j = 0; j < 10; j++)
for (int i = 1; i <= 100000000; i++)
try {
t.method1(i);
} catch (Exception ignored) {
}
l = System.currentTimeMillis() - l;
System.out.println("with try/catch method1 took " + l + " ms, result was " + t.getValue());
}
}
打印
with try/catch method1 took 9723 ms, result was 2
without try/catch method1 took 9456 ms, result was 2
with try/catch method1 took 9672 ms, result was 2
without try/catch method1 took 9476 ms, result was 2
with try/catch method1 took 8375 ms, result was 2
without try/catch method1 took 8233 ms, result was 2
with try/catch method1 took 8337 ms, result was 2
without try/catch method1 took 8227 ms, result was 2
with try/catch method1 took 8163 ms, result was 2
without try/catch method1 took 8565 ms, result was 2
从这些结果来看,似乎使用try / catch稍慢,但并非总是如此。
在Windows 7,Xeon E5450和Java 7更新7上运行。
答案 1 :(得分:5)
我用Caliper Microbenchmark尝试过,我真的看不出差异。
以下是代码:
public class TryCatchBenchmark extends SimpleBenchmark {
private int value;
public void setUp() {
value = 0;
}
// Calculates without exception
public void method1(int i) {
value = ((value + i) / i) << 1;
// Will never be true
if ((i & 0xFFFFFFF) == 1000000000) {
System.out.println("You'll never see this!");
}
}
public void timeWithoutTryCatch(int reps) {
for (int i = 1; i < reps; i++) {
this.method1(i);
}
}
public void timeWithTryCatch(int reps) {
for (int i = 1; i < reps; i++) {
try {
this.method1(i);
} catch (Exception ignore) {
}
}
}
public static void main(String[] args) {
new Runner().run(TryCatchBenchmark.class.getName());
}
}
结果如下:
0% Scenario{vm=java, trial=0, benchmark=WithoutTryCatch} 8,23 ns; σ=0,03 ns @ 3 trials 50% Scenario{vm=java, trial=0, benchmark=WithTryCatch} 8,13 ns; σ=0,03 ns @ 3 trials benchmark ns linear runtime WithoutTryCatch 8,23 ============================== WithTryCatch 8,13 =============================
如果我交换了函数的顺序(让它们以相反的顺序运行),结果是:
0% Scenario{vm=java, trial=0, benchmark=WithTryCatch} 8,21 ns; σ=0,05 ns @ 3 trials 50% Scenario{vm=java, trial=0, benchmark=WithoutTryCatch} 8,14 ns; σ=0,03 ns @ 3 trials benchmark ns linear runtime WithTryCatch 8,21 ============================== WithoutTryCatch 8,14 =============================
我会说它们基本相同。
答案 2 :(得分:2)
我做了一些实验。
首先,我完全确认了OP的发现。即使删除第一个循环,或者将异常更改为一些完全不相关的循环,try catch,只要不通过重新抛出异常来添加分支,就会使代码更快。如果代码真的必须捕获异常(如果你让循环从0开始而不是1),那么代码仍然会更快。
我的“解释”是JIT是狂野的优化机器,有时它们比其他时候表现更好,如果没有JIT级别的特定研究,你通常无法理解。有许多可能发生的变化(例如使用寄存器)。
This is globally what was found in a very similar case with a C# JIT.
在任何情况下,Java都针对try-catch进行了优化。由于始终存在异常的可能性,因此通过添加try-catch实际上并没有添加太多分支,因此不会发现第二个循环比第一个循环更长也就不足为奇了。
答案 3 :(得分:1)
为了避免JVM和操作系统可以执行任何隐藏的优化或缓存,我首先开发了两个种子java程序TryBlock
和NoTryBlock
,其中它们的区别在于使用try块或不。这两个种子程序将用于生成不同的程序,以禁止JVM或OS进行隐藏优化。在每个测试中,将生成并编译一个新的java程序,并重复测试10次。
根据我的实验,不使用try块运行平均需要9779.3 ms,而使用try block运行需要9775.9ms:平均运行时间差异为3.4ms(或0.035%),可视为噪音。这表明使用void try块(通过void,我的意思是除了空指针异常以外没有可能的异常)或者似乎没有对运行时间产生影响。
测试在同一台Linux机器(cpu 2392MHz)和java版本“1.6.0_24”下运行。
以下是基于种子程序生成测试程序的脚本:
for i in `seq 1 10`; do
echo "NoTryBlock$i"
cp NoTryBlock.java NoTryBlock$i.java
find . -name "NoTryBlock$i.java" -print | xargs sed -i "s/NoTryBlock/NoTryBlock$i/g";
javac NoTryBlock$i.java;
java NoTryBlock$i
rm NoTryBlock$i.* -f;
done
for i in `seq 1 10`; do
echo "TryBlock$i"
cp TryBlock.java TryBlock$i.java
find . -name "TryBlock$i.java" -print | xargs sed -i "s/TryBlock/TryBlock$i/g";
javac TryBlock$i.java;
java TryBlock$i
rm TryBlock$i.* -f;
done
以下是种子计划,首先是NoTryBlock.java
import java.util.*;
import java.lang.*;
public class NoTryBlock {
int value;
public int getValue() {
return value;
}
public void reset() {
value = 0;
}
// Calculates without exception
public void method1(int i) {
value = ((value + i) / i) << 1;
// Will never be true
if ((i & 0xFFFFFFF) == 1000000000) {
System.out.println("You'll never see this!");
}
}
public static void main(String[] args) {
int i, j;
long l;
NoTryBlock t = new NoTryBlock();
// using a try block
l = System.currentTimeMillis();
t.reset();
for (j = 1; j < 10; ++j) {
for (i = 1; i < 100000000; i++) {
t.method1(i);
}
}
l = System.currentTimeMillis() - l;
System.out.println(
"method1 with try block took " + l + " ms, result was "
+ t.getValue());
}
}
第二个是TryBlock.java
,它在方法函数调用上使用try-block:
import java.util.*;
import java.lang.*;
public class TryBlock {
int value;
public int getValue() {
return value;
}
public void reset() {
value = 0;
}
// Calculates without exception
public void method1(int i) {
value = ((value + i) / i) << 1;
// Will never be true
if ((i & 0xFFFFFFF) == 1000000000) {
System.out.println("You'll never see this!");
}
}
public static void main(String[] args) {
int i, j;
long l;
TryBlock t = new TryBlock();
// using a try block
l = System.currentTimeMillis();
t.reset();
for (j = 1; j < 10; ++j) {
for (i = 1; i < 100000000; i++) {
try {
t.method1(i);
} catch (Exception e) {
}
}
}
l = System.currentTimeMillis() - l;
System.out.println(
"method1 with try block took " + l + " ms, result was "
+ t.getValue());
}
}
下面是我的两个种子程序的差异,你可以看到除了类名,try块是他们唯一的区别:
$ diff TryBlock.java NoTryBlock.java
4c4
< public class TryBlock {
---
> public class NoTryBlock {
27c27
< TryBlock t = new TryBlock();
---
> NoTryBlock t = new NoTryBlock();
34d33
< try {
36,37d34
< } catch (Exception e) {
< }
42c39
< "method1 with try block took " + l + " ms, result was "
---
> "method1 without try block took " + l + " ms, result was "
以下是输出:
method1 without try block took,9732,ms, result was 2
method1 without try block took,9756,ms, result was 2
method1 without try block took,9845,ms, result was 2
method1 without try block took,9794,ms, result was 2
method1 without try block took,9758,ms, result was 2
method1 without try block took,9733,ms, result was 2
method1 without try block took,9763,ms, result was 2
method1 without try block took,9893,ms, result was 2
method1 without try block took,9761,ms, result was 2
method1 without try block took,9758,ms, result was 2
method1 with try block took,9776,ms, result was 2
method1 with try block took,9751,ms, result was 2
method1 with try block took,9767,ms, result was 2
method1 with try block took,9726,ms, result was 2
method1 with try block took,9779,ms, result was 2
method1 with try block took,9797,ms, result was 2
method1 with try block took,9845,ms, result was 2
method1 with try block took,9784,ms, result was 2
method1 with try block took,9787,ms, result was 2
method1 with try block took,9747,ms, result was 2