我正在使用一些代码,计算计算一些Java代码所需的时间,以获得一些Java功能的效率或低效的感觉。这样做,我现在陷入了一些非常奇怪的效果,我只是不能解释我自己。也许你们中有人能帮我理解一下。
public class PerformanceCheck {
public static void main(String[] args) {
List<PerformanceCheck> removeList = new LinkedList<PerformanceCheck>();
int maxTimes = 1000000000;
for (int i=0;i<10;i++) {
long time = System.currentTimeMillis();
for (int times=0;times<maxTimes;times++) {
// PERFORMANCE CHECK BLOCK START
if (removeList.size() > 0) {
testFunc(3);
}
// PERFORMANCE CHECK BLOCK END
}
long timeNow = System.currentTimeMillis();
System.out.println("time: " + (timeNow - time));
}
}
private static boolean testFunc(int test) {
return 5 > test;
}
}开始执行此操作将导致相对较长的计算时间(请记住,removeList为空,因此甚至不调用testFunc ):
time: 2328
time: 2223
...而将removeList.size() >0和testFunc(3)的任何组合替换为其他任何组合都会得到更好的结果。例如:
...
if (removeList.size() == 0) {
testFunc(3);
}
...结果(每次都会调用testFunc):
time: 8
time: 7
time: 0
time: 0即使相互独立地调用这两个函数,也会导致较短的计算时间:
...
if (removeList.size() == 0);
testFunc(3);
...结果:
time: 6
time: 5
time: 0
time: 0
...在我的初始示例中,只有这种特殊的组合才需要这么长时间。这让我很恼火,我真的很想理解它。它有什么特别之处?
谢谢。
添加:
更改第一个示例中的testFunc()
if (removeList.size() > 0) {
testFunc(times);
}到其他东西,比如
private static int testFunc2(int test) {
return 5*test;
}将导致再次变得更快。
发布于 2012-01-20 03:16:37
这真的很令人惊讶。生成的字节码除了条件之外是相同的,即ifle和ifne。
如果您使用-Xint关闭JIT,结果会更加合理。第二个版本要慢2倍。所以这与JIT优化有关。
我假设它可以在第二种情况下优化检查,但不能优化第一种情况(无论是什么原因)。尽管这意味着它完成了函数的工作,但缺少这个条件会让事情变得更快。它避免了管道停滞之类的问题。
发布于 2012-01-20 05:00:02
虽然与这个问题没有直接关系,但这就是如何使用Caliper正确地对代码进行微基准测试。下面是您的代码的修改版本,以便它可以与Caliper一起运行。内部循环必须进行一些修改,以便VM不会优化它们。在意识到什么都没有发生时,它出人意料地聪明。
在对Java代码进行基准测试时,也有许多细微差别。我写了一些我在Java Matrix Benchmark遇到的问题,比如过去的历史如何影响现在的结果。通过使用Caliper,您可以避免许多这样的问题。
公共类PerformanceCheck扩展了SimpleBenchmark { public int timeFirstCase(int reps) { List removeList =新的LinkedList();removeList.add(新的PerformanceCheck());int ret = 0;for( int i= 0;i< reps;i++ ){ if ( removeList.size() > 0) { if ( testFunc(i) ) ret++;}} return ret;} public int reps( int reps) {timeSecondCase removeList = LinkedList();removeList.add( new PerformanceCheck());int ret = 0;for( int i= 0;i< reps;i++ ){ if ( removeList.size() == 0) { if ( testFunc(i) ) ret++;}} return ret;}私有静态布尔测试(Int TestFunc){ return 5>测试;}公共静态空main(String[] args) { Runner.main(PerformanceCheck.class,args);} }
输出:
0% Scenario{vm=java, trial=0, benchmark=FirstCase} 0.60 ns; σ=0.00 ns @ 3 trials
50% Scenario{vm=java, trial=0, benchmark=SecondCase} 1.92 ns; σ=0.22 ns @ 10 trials
benchmark ns linear runtime
FirstCase 0.598 =========
SecondCase 1.925 ==============================
vm: java
trial: 0发布于 2012-01-20 03:59:06
嗯,我很高兴不必处理Java性能优化。我自己在64位Java JDK 7中尝试过。结果是任意的;)。无论我使用的是哪个列表,或者是否在进入循环之前缓存size()的结果,都没有区别。此外,完全清除测试函数几乎没有区别(因此它也不能成为分支预测命中)。优化标志可以提高性能,但也是任意的。
这里唯一的逻辑结果是,JIT编译器有时能够优化掉语句(这并不是很难实现),但它似乎相当武断。我更喜欢像C++这样的语言的众多原因之一,在这些语言中,行为至少是确定性的,即使它有时是任意的。
顺便说一句,在最新的Eclipse中,就像它一直在Windows上一样,通过IDE "Run“(无调试)运行这段代码比从控制台运行它慢10倍,所以关于这一点……
https://stackoverflow.com/questions/8931754
复制相似问题