当前位置: 首页 > 知识库问答 >
问题:

若迭代次数增加,则顺序流比并行流快

林铭
2023-03-14

我用最后的示例代码来衡量性能。

如果我调用参数numberOfTimes设置为100的checkPerformanceResult方法,并行流的性能将优于顺序流(sequential=346,parallel=78)。

如果我将参数设置为1000,则顺序流明显优于并行流(顺序=3239,并行=9337)。

我跑了很多次,结果都是一样的。

有人能给我解释一下这种行为以及引擎盖下到底发生了什么吗?

public class ParallelStreamExample {
    public static long checkPerformanceResult(Supplier<Integer> s, int numberOfTimes) {
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < numberOfTimes; i++) {

           s.get();
        }
        long endTime = System.currentTimeMillis();
        return endTime - startTime;
    }

    public static int sumSequentialStreamThread() {
        IntStream.rangeClosed(1, 10000000).sum();
        return 0;
    }

    public static int sumParallelStreamThread() {
        IntStream.rangeClosed(1, 10000000)
                .parallel().sum();
        return 0;
    }

    public static void main(String[] args) {
        System.out.println(checkPerformanceResult(ParallelStreamExample::sumSequentialStreamThread, 1000));
        System.out.println("break");
        System.out.println(checkPerformanceResult(ParallelStreamExample::sumParallelStreamThread, 1000));
    }
}

共有1个答案

陆子默
2023-03-14

使用线程并不总是使代码运行得更快

当使用几个线程时,管理每个线程总是有开销(将CPU时间分配给操作系统给每个线程,管理上下文切换时需要运行的下一行代码等等)

在这种特殊情况下

sumParallelStreamThread中创建的每个线程都执行非常简单的内存操作(调用返回数字的函数)。

所以SumSequentialStreamThreadSumParallelStreamThread之间的区别在于,在SumParallelStreamThread中,每个简单的操作都有创建线程并运行它的开销(假设后台没有任何线程优化)。

SumSequentialStreamThread做同样的事情,没有管理所有线程的开销,这就是为什么它运行得更快。

何时使用线程

处理线程最常见的用例是当您需要执行一系列I/O任务时。

什么是I/O任务?

这取决于几个因素,你可以在这里找到关于它的争论。但是我想通常人们会同意,向某个地方发出和执行HTTP请求或者执行数据库查询可以被认为是一个输入/输出操作。

为什么更适合呢?

因为I/O操作通常有一段时间等待与它们相关的响应。例如,当查询数据库时,执行查询的线程将等待数据库返回响应(即使其小于半秒),而此线程等待不同的线程可以执行其他操作,这就是我们可以获得性能的地方。

我发现,通常在不同的线程中运行只涉及内存和CPU操作的任务会使代码比一个线程运行得慢。

基准讨论

关于评论中的基准备注,我不确定它们是否正确,但在这种情况下,我会根据任何分析工具(或只是开始使用)仔细检查我的基准,如JProfiler或YoutKit,它们通常非常准确。

 类似资料:
  • 我有一个记录课程: 我创建了一个包含很多记录的大列表。只有第二个和第五个值,即i/10000和i,稍后分别由getter使用。 请注意,前10000条记录的类别2为0,接下来的10000条记录的类别1等,而值1按顺序为0-114999。 我创建了一个既并行又排序的流。 我有一个ForkJoinPool,它维护8个线程,这是我电脑上的内核数。 我使用这里描述的技巧将流处理任务提交给我自己的,而不是常

  • 问题 你有一系列排序序列,想将它们合并后得到一个排序序列并在上面迭代遍历。 解决方案 heapq.merge() 函数可以帮你解决这个问题。比如: >>> import heapq >>> a = [1, 4, 7, 10] >>> b = [2, 5, 6, 11] >>> for c in heapq.merge(a, b): ... print(c) ... 1 2 4 5 6 7

  • 返回的迭代器是否保证按该顺序提供值 、、? 我知道和保证集合的值顺序正确。此外,我并不是在问如何从迭代器生成流。

  • 考虑到我有2个CPU核心的事实,并行版本不是应该更快吗?有人能给我一个提示为什么并行版本比较慢吗?

  • 在Java8中,类没有任何方法来包装。 相反,我将从获取,然后从获取一个,如下所示:

  • 我有一个顺序数据源,表示为简单迭代器(或流)。数据相当大,不适合内存。此外,源代码可以遍历一次,并且获取成本很高。该源用于一些重过程(黑盒),该过程将迭代器(或流)作为其参数来使用线性数据。好的,很简单。但如果我有两种不同的消费程序,我该怎么办??正如我所说的,我不想将输入数据吸入类似列表的集合中。我也可以从一开始就重读源代码两次来完成我的任务,但我不喜欢这样,因为这样做没有效果。如果事实上我需要