Java中迭代、流(stream)、并行流(parallelStream)效率简单测试

在查阅示例代码时,看到Java流库,回故下其简洁的语法要比循环迭代优美、易读很多。

1. 测试

一时好奇想简单测试下迭代、流、并行流的运行效率。

测试环境:

计算机:DELL Inspiron 7572
处理器:Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz 1.99GHz
内存:16GB
系统类型:Window10 64位 

测试代码如下:

package study;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

public class CountLongWords {
    public static void main(String[] args) throws IOException {
        //for 效率测试
        long from = System.currentTimeMillis();
        String contents = new String(Files.readAllBytes(
                Paths.get("test.txt")), StandardCharsets.UTF_8);
        List words = Arrays.asList(contents.split("\\PL+"));
        long count = 0;
        for (String w : words){
            if (w.length() > 5){
                count++;
            }
        }
        long to = System.currentTimeMillis();
        System.out.println("long word count : " + count);
        System.out.println("time1 : " + (to - from) + " ms");

        //stream 效率测试
        from = System.currentTimeMillis();
        String contents1 = new String(Files.readAllBytes(
                Paths.get("test1.txt")), StandardCharsets.UTF_8);
        List words1 = Arrays.asList(contents.split("\\PL+"));
        count = words1.stream().filter(w -> w.length() > 5).count();
        to = System.currentTimeMillis();
        System.out.println("stream long word count : " + count);
        System.out.println("time2 : " + (to - from) + " ms");

        //parallelStream 效率测试
        from = System.currentTimeMillis();
        String contents2 = new String(Files.readAllBytes(
                Paths.get("test2.txt")), StandardCharsets.UTF_8);
        List words2 = Arrays.asList(contents.split("\\PL+"));
        count = words2.parallelStream().filter(w -> w.length() > 5).count();
        to = System.currentTimeMillis();
        System.out.println("parallelStream long word count : " + count);
        System.out.println("time3 : " + (to - from) + " ms");
    }
}

其中:test.txt / test1.txt / test2.txt 是一样的英文内容文本,大小86.2MB。

2. 结果

测试1 测试2 测试3 测试4 测试5
迭代 2601 ms 2633 ms 2529 ms 2602 ms 2707 ms
串行流 1931 ms 1932 ms 1923 ms 1993 ms 1939 ms
并行流 1941 ms 2134 ms 2136 ms 2256 ms 2151 ms

3. 总结

使用流(Stream)或并行流(parallelStream)效率比循环迭代的效率高

4. 拓展

按理说并行效率应该要比串行效率要高,但仔细看测试结果,可以注意到串行流的效率却比并行流的效率要高一些。

经查阅资料,使用并行流(parallelStream)时需要注意其特性,并不是所有情景下并行流(parallelStream)都可以发挥其优势。

从java8开始,并行编程变得很容易,通过并行流(parallelStream),可以很轻松的实现多线程并行处理。但是,这里面有个性能“陷阱”,如果不注意,使用并行流的效果反而更差,这个陷阱是什么呢?

这个陷阱就是,并行流默认都是用同一个默认的ForkJoinPool,这个ForkJoinPool的线程数和CPU的核心数相同。如果是计算密集型的操作,直接使用是没有问题的,因为这个ForkJoinPool会将所有的CPU打满,系统资源是没有浪费的,但是,如果其中还有IO操作或等待操作,这个默认的ForkJoinPool只能消耗一部分CPU,而另外的并行流因为获取不到该ForkJoinPool的使用权,性能将大大降低。可见,默认的ForkJoinPool必须只能处理计算密集型的任务。

那么,对应非计算密集型的任务,改怎么处理呢? 这就需要我们使用自己创建的ForkJoinPool来执行任务,下面给出实例代码:

    ForkJoinPool forkJoinPool = new ForkJoinPool(8); 
    forkJoinPool.submit(()->{
        tasks.parallelStream().forEach(t->{
            try {
                String gdsstatus=transactionService.GetTransInfo(url, t.getTask_id());
                checkStatus(t.getTask_id(),t.getTask_status(),gdsstatus);
            } catch (Exception e) {
                System.out.println("EXCEPTION OCCOR IN TASK:"+t.getTask_id());
                e.printStackTrace();
            }

            System.out.println("NO:"+count.getAndIncrement()+" is done");

        });
    });

参考:java并行流的性能“陷阱”

你可能感兴趣的:(Java)