一个简单的 list.parallelStream() in java 8 stream 好像不做偷工?
A simple list.parallelStream() in java 8 stream does not seem to do work stealing?
从这个问题“
Will inner parallel streams be processed fully in parallel before considering parallelizing outer stream?",我知道流执行工作窃取。但是,我注意到它似乎经常不会发生。例如,如果我有一个包含 100,000 个元素的列表,并且我尝试处理它在 parallelStream() 中,我经常注意到我的大多数 CPU 内核都处于 "waiting" 状态。(注意:在列表中的 100,000 个元素中,有些元素需要很长时间处理时间,而其他人则很快;而且,列表是不平衡的,这就是为什么有些线程可能会 "unlucky" 并且有很多事情要做,而其他人则很幸运并且没有什么可做的原因。
因此,我的理论是 JIT 编译器将 100,000 个元素初始划分为 16 个线程(因为我有 16 个内核),但随后在每个线程中,它只执行一个简单的(顺序的)for 循环(因为那将是最有效的),因此永远不会发生工作窃取(这就是我所看到的)。
我认为 Will inner parallel streams be processed fully in parallel before considering parallelizing outer stream? 显示工作窃取的原因是有一个 OUTER 循环正在流式传输 和 一个 INNER LOOP 正在流式传输,等等在这种情况下,每个内部循环在 运行 时间进行评估,并将创建新任务,这些任务可以在 运行 时间分配给 "idle" 线程。想法?有没有什么我做错了 "force" 一个简单的 list.parallelStream() 来使用工作窃取? (我目前的解决方法是尝试根据各种试探法来平衡列表,以便每个线程通常看到相同数量的工作;但是,很难预测....)
这与 JIT 编译器无关,而与 Stream 的实现有关 API。它将工作负载分成块,这些块由工作线程顺序处理。一般策略是拥有比工作线程更多的作业以启用工作窃取,参见示例 ForkJoinTask.getSurplusQueuedTaskCount()
,它可用于实现这种自适应策略。
以下代码可用于检测当源为 ArrayList
:
时按顺序处理了多少元素
List<Object> list = new ArrayList<>(Collections.nCopies(10_000, ""));
System.out.println(System.getProperty("java.version"));
System.out.println(Runtime.getRuntime().availableProcessors());
System.out.println( list.parallelStream()
.collect(
() -> new ArrayList<>(Collections.singleton(0)),
(l,x) -> l.replaceAll(i -> i + 1),
List::addAll) );
在我当前的测试机器上,它打印:
1.8.0_60
4
[625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625]
因此块多于核心,以允许窃取工作。但是,一旦块的顺序处理开始,它就无法进一步拆分,因此当每个元素的执行时间差异很大时,此实现具有局限性。这总是一个权衡。
从这个问题“ Will inner parallel streams be processed fully in parallel before considering parallelizing outer stream?",我知道流执行工作窃取。但是,我注意到它似乎经常不会发生。例如,如果我有一个包含 100,000 个元素的列表,并且我尝试处理它在 parallelStream() 中,我经常注意到我的大多数 CPU 内核都处于 "waiting" 状态。(注意:在列表中的 100,000 个元素中,有些元素需要很长时间处理时间,而其他人则很快;而且,列表是不平衡的,这就是为什么有些线程可能会 "unlucky" 并且有很多事情要做,而其他人则很幸运并且没有什么可做的原因。
因此,我的理论是 JIT 编译器将 100,000 个元素初始划分为 16 个线程(因为我有 16 个内核),但随后在每个线程中,它只执行一个简单的(顺序的)for 循环(因为那将是最有效的),因此永远不会发生工作窃取(这就是我所看到的)。
我认为 Will inner parallel streams be processed fully in parallel before considering parallelizing outer stream? 显示工作窃取的原因是有一个 OUTER 循环正在流式传输 和 一个 INNER LOOP 正在流式传输,等等在这种情况下,每个内部循环在 运行 时间进行评估,并将创建新任务,这些任务可以在 运行 时间分配给 "idle" 线程。想法?有没有什么我做错了 "force" 一个简单的 list.parallelStream() 来使用工作窃取? (我目前的解决方法是尝试根据各种试探法来平衡列表,以便每个线程通常看到相同数量的工作;但是,很难预测....)
这与 JIT 编译器无关,而与 Stream 的实现有关 API。它将工作负载分成块,这些块由工作线程顺序处理。一般策略是拥有比工作线程更多的作业以启用工作窃取,参见示例 ForkJoinTask.getSurplusQueuedTaskCount()
,它可用于实现这种自适应策略。
以下代码可用于检测当源为 ArrayList
:
List<Object> list = new ArrayList<>(Collections.nCopies(10_000, ""));
System.out.println(System.getProperty("java.version"));
System.out.println(Runtime.getRuntime().availableProcessors());
System.out.println( list.parallelStream()
.collect(
() -> new ArrayList<>(Collections.singleton(0)),
(l,x) -> l.replaceAll(i -> i + 1),
List::addAll) );
在我当前的测试机器上,它打印:
1.8.0_60
4
[625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625]
因此块多于核心,以允许窃取工作。但是,一旦块的顺序处理开始,它就无法进一步拆分,因此当每个元素的执行时间差异很大时,此实现具有局限性。这总是一个权衡。