我想了解Java对连续循环做了什么样的优化。更确切地说,我正在尝试检查是否执行了循环融合。
从理论上讲,我期望这种优化不会自动完成,并且期望确认融合版本比具有两个循环的版本更快。
但是,在运行基准测试之后,结果显示两个独立(和连续)循环比完成所有工作的单个循环更快。
我已经尝试使用JMH创建基准测试并获得相同的结果。
我使用了javap命令,它显示生成的具有两个循环的源文件的字节码实际上对应于正在执行的两个循环(没有循环展开或执行其他优化)。
为BenchmarkMultipleLoops.java测量的代码:
private void work() {
List intermediate = new ArrayList<>();
List res = new ArrayList<>();
int totalLength = 0;
for (Capsule c : caps) {
if(c.getNumber() > 100000000){
intermediate.add(c);
}
}
for (Capsule c : intermediate) {
String s = "new_word" + c.getNumber();
res.add(s);
}
//Loop to assure the end result (res) is used for something
for(String s : res){
totalLength += s.length();
}
System.out.println(totalLength);
}
正在测量BenchmarkSingleLoop.java的代码:
private void work(){
List res = new ArrayList<>();
int totalLength = 0;
for (Capsule c : caps) {
if(c.getNumber() > 100000000){
String s = "new_word" + c.getNumber();
res.add(s);
}
}
//Loop to assure the end result (res) is used for something
for(String s : res){
totalLength += s.length();
}
System.out.println(totalLength);
}
这是Capsule.java的代码:
public class Capsule {
private int number;
private String word;
public Capsule(int number, String word) {
this.number = number;
this.word = word;
}
public int getNumber() {
return number;
}
@Override
public String toString() {
return "{" + number +
", " + word + '}';
}
}
caps是ArrayList< Capsule>在开头填充了这样的2000万个元素:
private void populate() {
Random r = new Random(3);
for(int n = 0; n < POPSIZE; n++){
int randomN = r.nextInt();
Capsule c = new Capsule(randomN, "word" + randomN);
caps.add(c);
}
}
在测量之前,执行预热阶段。
我将每个基准测试运行了10次,换句话说,每个基准测试执行work()方法10次,完成的平均时间如下(以秒为单位)。每次迭代后,GC都会执行几次睡眠:
> MultipleLoops:4.9661秒
> SingleLoop:7.2725秒
OpenJDK 1.8.0_144在Intel i7-7500U(Kaby Lake)上运行。
为什么MultipleLoops版本比SingleLoop版本更快,即使它必须遍历两个不同的数据结构?
更新1:
正如评论中所建议的那样,如果我在生成字符串时更改实现以计算totalLength,从而避免创建res列表,则单循环版本会变得更快。
但是,只引入了该变量,以便在创建结果列表后完成一些工作,以避免在没有对它们进行任何操作时丢弃这些元素。
换句话说,预期的结果是产生最终列表。但这个建议有助于更好地了解正在发生的事情。
结果:
> MultipleLoops:0.9339秒
> SingleLoop:0.66590005秒
更新2:
结果:
> MultipleLoops:7.397秒
> SingleLoop:8.092秒