我正在使用 junit 来运行这样的原始基准测试:
@Test
public void testTime() throws Exception{
LoadoutBase<?> loadout = new LoadoutStandard("AS7-D-DC");
final int iterations = 1000000;
long start = System.nanoTime();
int sum = 0;
for(int i = 0; i < iterations; ++i){
Iterator<Item> it = loadout.iterator();
while( it.hasNext() ){
sum += it.next().getNumCriticalSlots();
}
}
long end = System.nanoTime();
long time_it = end - start;
start = System.nanoTime();
sum = 0;
for(int i = 0; i < iterations; ++i){
for(Item item : loadout.getAllItems()){
sum += item.getNumCriticalSlots();
}
}
end = System.nanoTime();
long time_arrays = end - start;
System.out.println("it: " + time_it + " array: " + time_arrays + " diff: " + (double)time_it/time_arrays);
}
如果我设置 iterations=1000000
然后我得到
it: 792771504 array: 1109215387 diff: 0.7147137637029551
非常一致,但如果我设置 iterations=10000
然后我得到
it: 32365742 array: 28902811 diff: 1.1198129482976587
波动很大。 diff 参数在 0.7 到 1.2 之间的任何地方
任何人都可以阐明这里可能发生的事情吗?我应该选择哪种方法?
编辑:
我真正进行基准测试的是幕后工作。 getAllItems
创建一个新的 List<Item>
并通过使用 addAll
从 16 个子列表中获取所有项目来填充它. Iterator
该方法不构建此临时列表,而是跟踪它当前正在迭代这 16 个子列表中的哪些子列表,并具有使这 16 个子列表看起来像一个连续列表的逻辑。
最佳答案
由于您想测试使用 Iterator
和使用增强的 for 循环(它在幕后为您使用 Iterator
)之间的区别,那么您做错了.首先是因为 JIT 有足够的时间改进第二种方法的结果而不是第一种方法的结果,这里解释了其他几个原因:How do I write a correct micro-benchmark in Java? .通过执行这些操作(同样是 JIT 的结果),您会看到非常不同的结果:
- 添加一个循环,增加执行两次迭代的次数。这是一个涵盖所有这些代码的
for
循环。 - 将增强的
for
循环方法移动到之前执行。
为您的测试获得真实结果的最佳方法是将这些方法分成不同的方法,然后对每个方法进行测量(JVM 预热阶段和上一个 QA 中涵盖的其他内容除外)。此外,我建议您不要重新发明轮子并使用基于 JUnit 的适当基准框架。这是其中两个:
基准测试的相关问答:
关于java - 这个 Java 基准测试发生了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25465881/