Java Stream 性能测试

Java Stream 性能测试

本测试是测试 Java Stream 与传统编程方式(即 foreach)之间的性能差距。

测试环境

JDK

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

测试框架

JMH v1.33

测试机器

  • CPU:AMD Ryzen 7 3700X 8 核 16 线程 4.4GHz
  • 内存:8GB * 2 2400MHz
  • 操作系统:Linux 5.15.7-1-MANJARO #1 SMP PREEMPT Wed Dec 8 10:09:19 UTC 2021 x86_64 GNU/Linux

测试方式

  • 用 maven 把项目打成 jar 包,在命令行界面运行这个 jar 包
  • 每个测试项目预热 3 次,每次持续 5 秒
  • 每个测试项目运行 5 次,每次持续 5 秒
  • 测试结果里的是两种方式以及不同 size 值情况下的平均耗时
  • 后面还有根据测试数据所制作的图表,图表中的比值指的是:Stream方式的平均耗时 / 传统方式的平均耗时

运行命令

java -server -Xms4g -jar stream-demo-1.0-SNAPSHOT-jar-with-dependencies.jar

测试场景

测试数据类:

public class User {
    private Integer id;
    private String username;
    private Boolean sex;
    private Integer type;
    ...
}

场景一

将用户列表转成 id -> username 的映射

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PerformanceTest {

    @Param({
        "500", "1000", "1500", "2000", "2500", "3000", "3500", "4000", "4500", "5000",
        "10000", "20000", "30000", "40000", "50000", "60000", "70000", "80000", "90000", "100000"
    })
    private int size;

    private List<User> users;

    @Setup
    public void setup() {
        users = initUsers(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public Map<Integer, String> tradition() {
        Map<Integer, String> map = new HashMap<>((int) (users.size() / 0.75));
        for (User user : users) {
            map.put(user.getId(), user.getUsername());
        }
        return map;
    }

    /**
     * Stream方式
     */
    @Benchmark
    public Map<Integer, String> stream() {
        Map<Integer, String> map = users.stream().collect(Collectors.toMap(
            User::getId, User::getUsername));
        return map;
    }

    private List<User> initUsers(int size) {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User(i, "user" + i, true, 0));
        }
        return users;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt     Score     Error  Units
PerformanceTest.stream        500  avgt    5     7.368 ±   0.097  us/op
PerformanceTest.stream       1000  avgt    5    14.940 ±   0.447  us/op
PerformanceTest.stream       1500  avgt    5    19.053 ±   0.761  us/op
PerformanceTest.stream       2000  avgt    5    30.343 ±   0.452  us/op
PerformanceTest.stream       2500  avgt    5    34.554 ±   1.179  us/op
PerformanceTest.stream       3000  avgt    5    38.740 ±   1.548  us/op
PerformanceTest.stream       3500  avgt    5    57.874 ±   2.010  us/op
PerformanceTest.stream       4000  avgt    5    61.074 ±   1.128  us/op
PerformanceTest.stream       4500  avgt    5    64.366 ±   1.613  us/op
PerformanceTest.stream       5000  avgt    5    70.140 ±   0.977  us/op
PerformanceTest.stream      10000  avgt    5   143.495 ±   7.884  us/op
PerformanceTest.stream      20000  avgt    5   288.530 ±  17.670  us/op
PerformanceTest.stream      30000  avgt    5   485.182 ±  14.847  us/op
PerformanceTest.stream      40000  avgt    5   569.877 ±  16.184  us/op
PerformanceTest.stream      50000  avgt    5   861.911 ±  36.345  us/op
PerformanceTest.stream      60000  avgt    5   984.600 ±  24.884  us/op
PerformanceTest.stream      70000  avgt    5  1073.787 ±  42.289  us/op
PerformanceTest.stream      80000  avgt    5  1202.904 ±  41.109  us/op
PerformanceTest.stream      90000  avgt    5  1323.942 ±  23.960  us/op
PerformanceTest.stream     100000  avgt    5  2222.201 ±  71.841  us/op
PerformanceTest.tradition     500  avgt    5     5.911 ±   0.148  us/op
PerformanceTest.tradition    1000  avgt    5    11.346 ±   0.546  us/op
PerformanceTest.tradition    1500  avgt    5    16.598 ±   0.947  us/op
PerformanceTest.tradition    2000  avgt    5    23.989 ±   0.908  us/op
PerformanceTest.tradition    2500  avgt    5    29.481 ±   1.090  us/op
PerformanceTest.tradition    3000  avgt    5    33.921 ±   4.120  us/op
PerformanceTest.tradition    3500  avgt    5    41.820 ±   4.523  us/op
PerformanceTest.tradition    4000  avgt    5    48.667 ±   2.597  us/op
PerformanceTest.tradition    4500  avgt    5    51.205 ±   3.530  us/op
PerformanceTest.tradition    5000  avgt    5    58.111 ±   2.269  us/op
PerformanceTest.tradition   10000  avgt    5   116.051 ±   6.215  us/op
PerformanceTest.tradition   20000  avgt    5   232.722 ±  24.085  us/op
PerformanceTest.tradition   30000  avgt    5   363.922 ±  23.553  us/op
PerformanceTest.tradition   40000  avgt    5   474.173 ±  17.631  us/op
PerformanceTest.tradition   50000  avgt    5   612.063 ±  42.738  us/op
PerformanceTest.tradition   60000  avgt    5   731.101 ±  78.408  us/op
PerformanceTest.tradition   70000  avgt    5   860.554 ±  47.381  us/op
PerformanceTest.tradition   80000  avgt    5   961.721 ± 181.393  us/op
PerformanceTest.tradition   90000  avgt    5  1093.675 ±  67.902  us/op
PerformanceTest.tradition  100000  avgt    5  1309.866 ±  43.078  us/op

图表

场景二

将用户列表转成 VO 列表

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PerformanceTest {

    @Param({
        "500", "1000", "1500", "2000", "2500", "3000", "3500", "4000", "4500", "5000",
        "10000", "20000", "30000", "40000", "50000", "60000", "70000", "80000", "90000", "100000"
    })
    private int size;

    private List<User> users;

    @Setup
    public void setup() {
        users = initUsers(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public List<UserVO> tradition() {
        List<UserVO> userVOList = new ArrayList<>(users.size());
        for (User user : users) {
            userVOList.add(toUserVo(user));
        }
        return userVOList;
    }

    /**
     * Stream方式
     */
    @Benchmark
    public List<UserVO> stream() {
        List<UserVO> userVoList = users.stream()
            .map(this::toUserVo)
            .collect(Collectors.toList());
        return userVoList;
    }

    private UserVO toUserVo(User user) {
        UserVO userVO = new UserVO();
        userVO.setId(user.getId());
        userVO.setUsername(user.getUsername());
        userVO.setSex(getSexStr(user.getSex()));
        userVO.setType(getTypeStr(user.getType()));
        return userVO;
    }

    private String getSexStr(Boolean sex) {
        if (sex == null) return "";
        return sex ? "男" : "女";
    }

    private String getTypeStr(Integer type) {
        if (type == null) return "";
        switch (type) {
            case 0:
                return "青铜用户";
            case 1:
                return "白银用户";
            case 2:
                return "黄金用户";
            default:
                return "";
        }
    }

    private List<User> initUsers(int size) {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User(i, "user" + i, true, 0));
        }
        return users;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt     Score    Error  Units
PerformanceTest.stream        500  avgt    5     5.996 ±  0.066  us/op
PerformanceTest.stream       1000  avgt    5    12.553 ±  0.238  us/op
PerformanceTest.stream       1500  avgt    5    17.990 ±  0.517  us/op
PerformanceTest.stream       2000  avgt    5    25.438 ±  0.721  us/op
PerformanceTest.stream       2500  avgt    5    29.965 ±  1.067  us/op
PerformanceTest.stream       3000  avgt    5    35.679 ±  0.844  us/op
PerformanceTest.stream       3500  avgt    5    41.705 ±  2.245  us/op
PerformanceTest.stream       4000  avgt    5    47.160 ±  2.103  us/op
PerformanceTest.stream       4500  avgt    5    54.689 ±  1.613  us/op
PerformanceTest.stream       5000  avgt    5    59.484 ±  1.003  us/op
PerformanceTest.stream      10000  avgt    5   125.990 ±  2.135  us/op
PerformanceTest.stream      20000  avgt    5   237.947 ±  6.158  us/op
PerformanceTest.stream      30000  avgt    5   364.168 ±  7.328  us/op
PerformanceTest.stream      40000  avgt    5   505.319 ±  7.990  us/op
PerformanceTest.stream      50000  avgt    5   629.942 ± 20.899  us/op
PerformanceTest.stream      60000  avgt    5   746.447 ± 26.372  us/op
PerformanceTest.stream      70000  avgt    5   863.620 ± 15.332  us/op
PerformanceTest.stream      80000  avgt    5  1039.621 ± 14.531  us/op
PerformanceTest.stream      90000  avgt    5  1134.118 ± 21.441  us/op
PerformanceTest.stream     100000  avgt    5  1198.103 ± 31.366  us/op
PerformanceTest.tradition     500  avgt    5     4.069 ±  0.053  us/op
PerformanceTest.tradition    1000  avgt    5     8.143 ±  0.085  us/op
PerformanceTest.tradition    1500  avgt    5    12.278 ±  0.268  us/op
PerformanceTest.tradition    2000  avgt    5    16.309 ±  0.297  us/op
PerformanceTest.tradition    2500  avgt    5    20.802 ±  0.855  us/op
PerformanceTest.tradition    3000  avgt    5    24.092 ±  0.146  us/op
PerformanceTest.tradition    3500  avgt    5    28.964 ±  0.252  us/op
PerformanceTest.tradition    4000  avgt    5    32.585 ±  0.533  us/op
PerformanceTest.tradition    4500  avgt    5    37.213 ±  0.533  us/op
PerformanceTest.tradition    5000  avgt    5    41.788 ±  0.668  us/op
PerformanceTest.tradition   10000  avgt    5    83.733 ±  8.949  us/op
PerformanceTest.tradition   20000  avgt    5   167.552 ±  5.889  us/op
PerformanceTest.tradition   30000  avgt    5   246.840 ± 41.616  us/op
PerformanceTest.tradition   40000  avgt    5   311.422 ± 58.097  us/op
PerformanceTest.tradition   50000  avgt    5   373.161 ±  3.911  us/op
PerformanceTest.tradition   60000  avgt    5   464.576 ±  5.978  us/op
PerformanceTest.tradition   70000  avgt    5   524.914 ±  4.203  us/op
PerformanceTest.tradition   80000  avgt    5   624.687 ± 11.820  us/op
PerformanceTest.tradition   90000  avgt    5   700.555 ±  4.651  us/op
PerformanceTest.tradition  100000  avgt    5   864.347 ± 15.987  us/op

图表

场景三

将用户列表的 username 按照在列表中的顺序用逗号拼接起来,并去重

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PerformanceTest {

    @Param({
        "500", "1000", "1500", "2000", "2500", "3000", "3500", "4000", "4500", "5000",
        "10000", "20000", "30000", "40000", "50000", "60000", "70000", "80000", "90000", "100000"
    })
    private int size;

    private List<User> users;

    @Setup
    public void setup() {
        users = initUsers(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public String tradition() {
        StringBuilder usernames = new StringBuilder();
        Set<String> set = new HashSet<>();
        for (User user : users) {
            if (set.add(user.getUsername())) {
                usernames.append(user.getUsername()).append(',');
            }
        }
        if (usernames.length() != 0) {
            usernames.deleteCharAt(usernames.length() - 1);
        }
        return usernames.toString();
    }

    /**
     * Stream方式
     */
    @Benchmark
    public String stream() {
        String usernames = users.stream()
            .map(User::getUsername)
            .distinct()
            .collect(Collectors.joining(","));
        return usernames;
    }

    private List<User> initUsers(int size) {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User(i, "user" + i, true, 0));
        }
        return users;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt     Score     Error  Units
PerformanceTest.stream        500  avgt    5    18.733 ±   0.128  us/op
PerformanceTest.stream       1000  avgt    5    36.986 ±   0.688  us/op
PerformanceTest.stream       1500  avgt    5    55.284 ±   2.427  us/op
PerformanceTest.stream       2000  avgt    5    75.768 ±   3.998  us/op
PerformanceTest.stream       2500  avgt    5    97.292 ±   3.165  us/op
PerformanceTest.stream       3000  avgt    5   112.935 ±   3.832  us/op
PerformanceTest.stream       3500  avgt    5   143.662 ±   3.926  us/op
PerformanceTest.stream       4000  avgt    5   157.015 ±   5.791  us/op
PerformanceTest.stream       4500  avgt    5   181.353 ±   3.722  us/op
PerformanceTest.stream       5000  avgt    5   200.123 ±   2.760  us/op
PerformanceTest.stream      10000  avgt    5   421.966 ±  11.457  us/op
PerformanceTest.stream      20000  avgt    5   903.303 ±   9.818  us/op
PerformanceTest.stream      30000  avgt    5  1431.774 ±  11.031  us/op
PerformanceTest.stream      40000  avgt    5  1982.262 ±   9.562  us/op
PerformanceTest.stream      50000  avgt    5  2703.652 ±  47.758  us/op
PerformanceTest.stream      60000  avgt    5  3170.086 ± 164.340  us/op
PerformanceTest.stream      70000  avgt    5  4267.913 ± 121.917  us/op
PerformanceTest.stream      80000  avgt    5  4803.814 ±  96.398  us/op
PerformanceTest.stream      90000  avgt    5  5333.996 ±  57.002  us/op
PerformanceTest.stream     100000  avgt    5  6855.313 ±  40.467  us/op
PerformanceTest.tradition     500  avgt    5    13.903 ±   0.314  us/op
PerformanceTest.tradition    1000  avgt    5    28.263 ±   0.722  us/op
PerformanceTest.tradition    1500  avgt    5    43.187 ±   0.666  us/op
PerformanceTest.tradition    2000  avgt    5    58.980 ±   1.020  us/op
PerformanceTest.tradition    2500  avgt    5    76.938 ±   1.833  us/op
PerformanceTest.tradition    3000  avgt    5    88.327 ±   1.034  us/op
PerformanceTest.tradition    3500  avgt    5   112.626 ±   1.048  us/op
PerformanceTest.tradition    4000  avgt    5   124.694 ±   2.813  us/op
PerformanceTest.tradition    4500  avgt    5   146.171 ±   1.465  us/op
PerformanceTest.tradition    5000  avgt    5   158.249 ±   0.645  us/op
PerformanceTest.tradition   10000  avgt    5   336.988 ±  10.747  us/op
PerformanceTest.tradition   20000  avgt    5   705.727 ±   6.379  us/op
PerformanceTest.tradition   30000  avgt    5  1146.510 ±   7.255  us/op
PerformanceTest.tradition   40000  avgt    5  1496.571 ±  24.538  us/op
PerformanceTest.tradition   50000  avgt    5  2251.932 ±  13.162  us/op
PerformanceTest.tradition   60000  avgt    5  2706.076 ±  37.305  us/op
PerformanceTest.tradition   70000  avgt    5  3424.254 ±  27.254  us/op
PerformanceTest.tradition   80000  avgt    5  3845.747 ±  97.428  us/op
PerformanceTest.tradition   90000  avgt    5  4340.254 ±  26.115  us/op
PerformanceTest.tradition  100000  avgt    5  5540.504 ±  10.432  us/op

图表

场景四

统计整型数组中的偶数个数

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PerformanceTest {

    @Param({
        "500", "1000", "1500", "2000", "2500", "3000", "3500", "4000", "4500", "5000",
        "10000", "20000", "30000", "40000", "50000", "60000", "70000", "80000", "90000", "100000"
    })
    private int size;

    private int[] array;

    @Setup
    public void setup() {
        array = initArray(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public int tradition() {
        int count = 0;
        for (int i : array) {
            if (i % 2 == 0) {
                count++;
            }
        }
        return count;
    }

    /**
     * Stream方式
     */
    @Benchmark
    public int stream() {
        long count = Arrays.stream(array)
            .filter(i -> i % 2 == 0)
            .count();
        return (int) count;
    }

    private int[] initArray(int size) {
        int[] array = new int[size];
        for (int i = 0; i < size; i++) {
            array[i] = i;
        }
        return array;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt    Score     Error  Units
PerformanceTest.stream        500  avgt    5    0.475 ±   0.010  us/op
PerformanceTest.stream       1000  avgt    5    0.919 ±   0.010  us/op
PerformanceTest.stream       1500  avgt    5    1.357 ±   0.033  us/op
PerformanceTest.stream       2000  avgt    5    1.788 ±   0.076  us/op
PerformanceTest.stream       2500  avgt    5    2.255 ±   0.007  us/op
PerformanceTest.stream       3000  avgt    5    2.828 ±   0.025  us/op
PerformanceTest.stream       3500  avgt    5    3.115 ±   0.025  us/op
PerformanceTest.stream       4000  avgt    5    3.531 ±   0.031  us/op
PerformanceTest.stream       4500  avgt    5    4.020 ±   0.022  us/op
PerformanceTest.stream       5000  avgt    5    4.491 ±   0.025  us/op
PerformanceTest.stream      10000  avgt    5    9.029 ±   1.425  us/op
PerformanceTest.stream      20000  avgt    5   18.201 ±   0.074  us/op
PerformanceTest.stream      30000  avgt    5   21.745 ±   0.117  us/op
PerformanceTest.stream      40000  avgt    5   30.214 ±   0.241  us/op
PerformanceTest.stream      50000  avgt    5   38.219 ±   2.246  us/op
PerformanceTest.stream      60000  avgt    5   55.953 ±   2.188  us/op
PerformanceTest.stream      70000  avgt    5   60.985 ±  11.136  us/op
PerformanceTest.stream      80000  avgt    5   65.562 ±  53.646  us/op
PerformanceTest.stream      90000  avgt    5  104.806 ± 296.831  us/op
PerformanceTest.stream     100000  avgt    5  141.883 ± 490.840  us/op
PerformanceTest.tradition     500  avgt    5    0.241 ±   0.004  us/op
PerformanceTest.tradition    1000  avgt    5    0.479 ±   0.009  us/op
PerformanceTest.tradition    1500  avgt    5    0.718 ±   0.003  us/op
PerformanceTest.tradition    2000  avgt    5    0.939 ±   0.001  us/op
PerformanceTest.tradition    2500  avgt    5    1.173 ±   0.001  us/op
PerformanceTest.tradition    3000  avgt    5    1.414 ±   0.036  us/op
PerformanceTest.tradition    3500  avgt    5    1.639 ±   0.002  us/op
PerformanceTest.tradition    4000  avgt    5    1.874 ±   0.002  us/op
PerformanceTest.tradition    4500  avgt    5    2.114 ±   0.055  us/op
PerformanceTest.tradition    5000  avgt    5    2.367 ±   0.002  us/op
PerformanceTest.tradition   10000  avgt    5    4.735 ±   0.237  us/op
PerformanceTest.tradition   20000  avgt    5    9.453 ±   0.010  us/op
PerformanceTest.tradition   30000  avgt    5   14.275 ±   0.547  us/op
PerformanceTest.tradition   40000  avgt    5   18.768 ±   0.487  us/op
PerformanceTest.tradition   50000  avgt    5   23.455 ±   1.177  us/op
PerformanceTest.tradition   60000  avgt    5   28.083 ±   0.434  us/op
PerformanceTest.tradition   70000  avgt    5   32.652 ±   0.039  us/op
PerformanceTest.tradition   80000  avgt    5   37.799 ±   0.036  us/op
PerformanceTest.tradition   90000  avgt    5   41.985 ±   0.368  us/op
PerformanceTest.tradition  100000  avgt    5   46.487 ±   0.180  us/op

图表

场景五

跟场景一一样,不过使用的是并行流

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 3, time = 5)
@Measurement(iterations = 5, time = 5)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PerformanceTest {

    @Param({
        "500", "1000", "1500", "2000", "2500", "3000", "3500", "4000", "4500", "5000",
        "10000", "20000", "30000", "40000", "50000", "60000", "70000", "80000", "90000", "100000"
    })
    private int size;

    private List<User> users;

    @Setup
    public void setup() {
        users = initUsers(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public Map<Integer, String> tradition() {
        Map<Integer, String> map = new HashMap<>((int) (users.size() / 0.75));
        for (User user : users) {
            map.put(user.getId(), user.getUsername());
        }
        return map;
    }

    /**
     * Stream方式
     */
    @Benchmark
    public Map<Integer, String> stream() {
        Map<Integer, String> map = users.parallelStream()
            .collect(Collectors.toMap(
                User::getId, User::getUsername));
        return map;
    }

    private List<User> initUsers(int size) {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User(i, "user" + i, true, 0));
        }
        return users;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt     Score    Error  Units  
PerformanceTest.stream        500  avgt    5    35.249 ±  0.201  us/op  
PerformanceTest.stream       1000  avgt    5    50.628 ±  0.142  us/op  
PerformanceTest.stream       1500  avgt    5    57.612 ±  0.110  us/op  
PerformanceTest.stream       2000  avgt    5    75.033 ±  0.326  us/op  
PerformanceTest.stream       2500  avgt    5    86.458 ±  0.305  us/op  
PerformanceTest.stream       3000  avgt    5    92.287 ±  0.414  us/op  
PerformanceTest.stream       3500  avgt    5   116.306 ±  0.359  us/op  
PerformanceTest.stream       4000  avgt    5   125.447 ±  0.174  us/op  
PerformanceTest.stream       4500  avgt    5   138.188 ±  0.525  us/op  
PerformanceTest.stream       5000  avgt    5   146.119 ±  0.443  us/op  
PerformanceTest.stream      10000  avgt    5   267.039 ±  1.097  us/op  
PerformanceTest.stream      20000  avgt    5   520.755 ±  4.113  us/op  
PerformanceTest.stream      30000  avgt    5   841.691 ±  2.198  us/op  
PerformanceTest.stream      40000  avgt    5  1088.443 ±  6.130  us/op  
PerformanceTest.stream      50000  avgt    5  1592.737 ± 12.963  us/op  
PerformanceTest.stream      60000  avgt    5  1824.027 ±  6.323  us/op  
PerformanceTest.stream      70000  avgt    5  2177.562 ± 12.864  us/op  
PerformanceTest.stream      80000  avgt    5  2472.056 ±  3.235  us/op  
PerformanceTest.stream      90000  avgt    5  2655.888 ± 13.620  us/op  
PerformanceTest.stream     100000  avgt    5  3897.188 ± 31.997  us/op  
PerformanceTest.tradition     500  avgt    5     5.653 ±  0.099  us/op  
PerformanceTest.tradition    1000  avgt    5    10.959 ±  0.219  us/op  
PerformanceTest.tradition    1500  avgt    5    17.192 ±  1.095  us/op  
PerformanceTest.tradition    2000  avgt    5    21.409 ±  0.627  us/op  
PerformanceTest.tradition    2500  avgt    5    28.927 ±  2.463  us/op  
PerformanceTest.tradition    3000  avgt    5    32.994 ±  1.276  us/op  
PerformanceTest.tradition    3500  avgt    5    42.240 ±  1.316  us/op  
PerformanceTest.tradition    4000  avgt    5    47.977 ±  6.044  us/op  
PerformanceTest.tradition    4500  avgt    5    52.298 ±  3.606  us/op  
PerformanceTest.tradition    5000  avgt    5    62.834 ±  7.198  us/op  
PerformanceTest.tradition   10000  avgt    5   117.203 ± 25.916  us/op  
PerformanceTest.tradition   20000  avgt    5   236.909 ± 20.363  us/op  
PerformanceTest.tradition   30000  avgt    5   367.157 ± 37.229  us/op  
PerformanceTest.tradition   40000  avgt    5   491.487 ± 31.215  us/op  
PerformanceTest.tradition   50000  avgt    5   609.987 ± 66.807  us/op  
PerformanceTest.tradition   60000  avgt    5   717.241 ± 50.279  us/op  
PerformanceTest.tradition   70000  avgt    5   869.001 ± 85.684  us/op  
PerformanceTest.tradition   80000  avgt    5   974.933 ± 72.376  us/op  
PerformanceTest.tradition   90000  avgt    5  1111.963 ± 38.911  us/op  
PerformanceTest.tradition  100000  avgt    5  1372.851 ± 53.405  us/op

图表

场景六

跟场景二一样,不过使用的是并行流,并且在 toUserVo 里增加了耗时操作的模拟

测试代码

@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 2, time = 30)
@Measurement(iterations = 2, time = 30)
@OutputTimeUnit(TimeUnit.SECONDS)
public class PerformanceTest {

    @Param({
        "5", "10", "15", "20", "25", "30", "35", "40", "45", "50"
    })
    private int size;

    private List<User> users;

    @Setup
    public void setup() {
        users = initUsers(size);
    }

    /**
     * 传统方式
     */
    @Benchmark
    public List<UserVO> tradition() {
        List<UserVO> userVOList = new ArrayList<>(users.size());
        for (User user : users) {
            userVOList.add(toUserVo(user));
        }
        return userVOList;
    }

    /**
     * Stream方式
     */
    @Benchmark
    public List<UserVO> stream() {
        List<UserVO> userVoList = users.parallelStream()
            .map(this::toUserVo)
            .collect(Collectors.toList());
        return userVoList;
    }

    private UserVO toUserVo(User user) {
        UserVO userVO = new UserVO();
        // 模拟耗时操作,在我的机器上这段代码的耗时约为100毫秒
//        long startAt = System.currentTimeMillis();
        for (long i = 0; i < 100000000L; i++) {
            userVO.setId(user.getId());
            userVO.setUsername(user.getUsername());
            userVO.setSex(getSexStr(user.getSex()));
            userVO.setType(getTypeStr(user.getType()));
        }
//        long time = System.currentTimeMillis() - startAt;
//        System.out.println("耗时:" + time);
        return userVO;
    }

    private String getSexStr(Boolean sex) {
        if (sex == null) return "";
        return sex ? "男" : "女";
    }

    private String getTypeStr(Integer type) {
        if (type == null) return "";
        switch (type) {
            case 0: return "青铜用户";
            case 1: return "白银用户";
            case 2: return "黄金用户";
            default: return "";
        }
    }

    private List<User> initUsers(int size) {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User(i, "user" + i, true, 0));
        }
        return users;
    }

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(PerformanceTest.class.getSimpleName())
            .build();
        new Runner(opt).run();
    }
}

测试结果

Benchmark                  (size)  Mode  Cnt  Score   Error  Units
PerformanceTest.stream          5  avgt    2  0.121           s/op
PerformanceTest.stream         10  avgt    2  0.287           s/op
PerformanceTest.stream         15  avgt    2  1.316           s/op
PerformanceTest.stream         20  avgt    2  0.811           s/op
PerformanceTest.stream         25  avgt    2  1.287           s/op
PerformanceTest.stream         30  avgt    2  2.604           s/op
PerformanceTest.stream         35  avgt    2  1.711           s/op
PerformanceTest.stream         40  avgt    2  3.839           s/op
PerformanceTest.stream         45  avgt    2  3.883           s/op
PerformanceTest.stream         50  avgt    2  3.237           s/op
PerformanceTest.tradition       5  avgt    2  0.578           s/op
PerformanceTest.tradition      10  avgt    2  1.166           s/op
PerformanceTest.tradition      15  avgt    2  1.740           s/op
PerformanceTest.tradition      20  avgt    2  2.304           s/op
PerformanceTest.tradition      25  avgt    2  2.877           s/op
PerformanceTest.tradition      30  avgt    2  3.440           s/op
PerformanceTest.tradition      35  avgt    2  4.045           s/op
PerformanceTest.tradition      40  avgt    2  4.634           s/op
PerformanceTest.tradition      45  avgt    2  5.162           s/op
PerformanceTest.tradition      50  avgt    2  5.801           s/op

图表

总结

  • 对于一般场景下的处理,Stream 方式的耗时大概是传统方式的 1.5 倍左右
  • 对于基本数据类型的处理,Stream 方式的耗时大概是传统方式在 2 倍左右
  • 对于计算量较少的情况,不建议使用并行流

使用 Stream 方式跟传统编程方式相比运行效率之间的差距并不大,除非是对性能要求很高的系统,否则对于一般的业务系统来说不会造成 CPU 的瓶颈,IO 才是最大的瓶颈。相比之下,使用 Stream 可以让代码变得更加干净整洁,可读性和可维护性更好,所以一般情况下推荐使用 Stream 来编程。

posted @ 2021-12-21 16:11  风飞飘杨  阅读(721)  评论(0)    收藏  举报