我想了解Java对连续for循环进行何种优化。更具体地说,我正在尝试检查是否执行了循环合并优化。
理论上,我原本期望这种优化不会自动执行,并期望确认合并版本是否比具有两个循环的版本更快。
然而,在运行基准测试后,结果显示两个分开的(且连续的)循环比一个完成所有工作的单个循环更快。
我已经尝试使用JMH创建基准测试,并得到了相同的结果。
我使用了javap
命令,它显示具有两个循环的源文件生成的字节码实际上对应于执行两个循环(没有展开循环或其他优化)。
被测量代码为BenchmarkMultipleLoops.java
:
private void work() {
List<Capsule> intermediate = new ArrayList<>();
List<String> 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);
}
< p > 对于< code >BenchmarkSingleLoop.java < /code > 进行测量的代码:< /p >private void work(){
List<String> 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
是一个有2000万个元素的ArrayList<Capsule>
,在初始时这些元素被填充成以下形式:
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秒
运行在一个Intel i7-7500U(Kaby Lake)上的OpenJDK 1.8.0_144。
为什么多循环版本比单循环版本更快,即使它必须遍历两个不同的数据结构?
更新1:
如评论中所建议的,如果我改变实现来计算字符串生成时的totalLength
,避免创建res
列表,则单循环版本变得更快。
但是,那个变量仅仅是为了在创建结果列表后执行一些工作,以避免在没有对其进行任何处理的情况下丢弃元素。
换句话说,目的是生成最终列表。但这个建议有助于更好地理解正在发生的事情。
结果:
- MultipleLoops:0.9339秒
- SingleLoop:0.66590005秒
更新2:
这是我用于JMH基准测试的代码链接:https://gist.github.com/FranciscoRibeiro/2d3928761f76e4f7cecfcfcdf7fc96d5
结果:
- MultipleLoops:7.397秒
- SingleLoop:8.092秒