foreach性能比较

现象引入

下面看一个循环的两种写法。之前从同事那里听到一个说法是,下面这种方式在执行循环过程中,在方法栈帧中只会创建一个局部变量,比方式一会比较省时间。

// 方式一: 变量放在循环体里面
for (int i = 0; i < count; i++) {
    Object obj = new Object();
}

// 方式二: 变量放在循环体外面
Object obj;
for (int i = 0; i < count; i++) {
    obj = new Object();
}

下面就来验证一下

测试案例

本文测试使用的jdk版本是jdk1.8.0_361

➜ java -version
java version "1.8.0_361"
Java(TM) SE Runtime Environment (build 1.8.0_361-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)

以下面这个代码为例

public class ForeachTest {

    final int count = 100000;

    public void inner() {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            Object obj = new Object();
            list.add(obj);
        }
    }

    public void outer() {
        List<Object> list = new ArrayList<>();
        Object obj;
        for (int i = 0; i < count; i++) {
            obj = new Object();
            list.add(obj);
        }
    }
}

测试几次的结果如下:
CPU 12th Gen Intel® Core™ i7-12700H 基准速度: 2.30 GHz
每个循环次数都跑了几次,取的平均值,结果如下:

循环次数innerouter
100 * 10003ms3ms
1000 * 100016ms16ms
10 * 1000 * 10002987ms297ms
100 * 1000 * 100028860ms40446ms

Benchmark

下面是Benchmark所用的测试代码,主要测试执行的平均时间这个指标

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(2)
public class JMHExample_Foreach {

    final int count = 1000000;

    @Benchmark
    public void inner() {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            Object obj = new Object();
            list.add(obj);
        }
    }

    @Benchmark
    public void outer() {
        List<Object> list = new ArrayList<>();
        Object obj;
        for (int i = 0; i < count; i++) {
            obj = new Object();
            list.add(obj);
        }
    }

    public static void main(String[] args) throws Exception {
        Options opts = new OptionsBuilder()
                .include(JMHExample_Foreach.class.getSimpleName()) // 指定测试的类
                .resultFormat(ResultFormatType.JSON)
                .build();
        new Runner(opts).run();
    }
}

下面是测试结果

  1. 循环100 *1000次
    Benchmark Mode Cnt Score Error Units
    JMHExample_Foreach.inner avgt 10 0.333 ± 0.037 ms/op
    JMHExample_Foreach.outer avgt 10 0.334 ± 0.040 ms/op
    在这里插入图片描述

  2. 循环1000 * 1000次
    Benchmark Mode Cnt Score Error Units
    JMHExample_Foreach.inner avgt 10 3.865 ± 0.036 ms/op
    JMHExample_Foreach.outer avgt 10 5.949 ± 2.471 ms/op
    在这里插入图片描述

  3. 循环10 * 1000 * 1000次
    Benchmark Mode Cnt Score Error Units
    JMHExample_Foreach.inner avgt 10 57.637 ± 4.896 ms/op
    JMHExample_Foreach.outer avgt 10 95.938 ± 27.384 ms/op
    在这里插入图片描述

  4. 循环100 * 1000 * 1000次
    Benchmark Mode Cnt Score Error Units
    JMHExample_Foreach.inner avgt 10 20455.711 ± 17706.877 ms/op
    JMHExample_Foreach.outer avgt 10 23232.470 ± 21912.028 ms/op
    在这里插入图片描述

现象分析

实际上这两个方法编译过后的结果都是一样的,下面是通过intellij反编译的结果

在这里插入图片描述

字节码对比

两个方法的字节码其实也差不多,从前面反编译的结果就能看出来了
inner方法

 0 new #4 <java/util/ArrayList>
 3 dup
 4 invokespecial #5 <java/util/ArrayList.<init> : ()V>
 7 astore_1
 8 iconst_0
 9 istore_2
10 iload_2
11 ldc #2 <100000000>
13 if_icmpge 38 (+25)
16 new #7 <java/lang/Object>
19 dup
20 invokespecial #1 <java/lang/Object.<init> : ()V>
23 astore_3
24 aload_1
25 aload_3
26 invokeinterface #8 <java/util/List.add : (Ljava/lang/Object;)Z> count 2
31 pop
32 iinc 2 by 1
35 goto 10 (-25)
38 return

outer

 0 new #4 <java/util/ArrayList>
 3 dup
 4 invokespecial #5 <java/util/ArrayList.<init> : ()V>
 7 astore_1
 8 iconst_0
 9 istore_3
10 iload_3
11 ldc #2 <100000000>
13 if_icmpge 38 (+25)
16 new #7 <java/lang/Object>
19 dup
20 invokespecial #1 <java/lang/Object.<init> : ()V>
23 astore_2
24 aload_1
25 aload_2
26 invokeinterface #8 <java/util/List.add : (Ljava/lang/Object;)Z> count 2
31 pop
32 iinc 3 by 1
35 goto 10 (-25)
38 return
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值