![](/img/trans.png)
[英]how to create execution plan where exported stream and siddhi query insert into have the same name?
[英]How does the Java Stream API select an execution plan?
我刚开始学习Java 8和一般功能编程中的Stream API,但对Java却不是新手。 我对了解和理解Stream API如何选择执行计划感兴趣。
它如何知道要并行化哪些部分以及不并行化哪些部分? 甚至有多少种执行计划?
基本上,我想知道Java 8中的Streams为什么有助于使事情更快,以及它如何完成某些“魔术”。
我找不到太多有关这一切如何运作的文献。
这个问题可以广泛解释,但我会尽力使它满意。 我也使用ArrayList流的示例。
当我们创建流时,返回的对象称为ReferencePipeline
。 可以说这个对象是“默认流”对象,因为它还没有任何功能。 现在我们必须在懒惰和渴望的方法之间做出选择。 因此,让我们来看一个示例。
示例一: filter(Predicate<?>)
方法:
filter()
方法的声明如下:
@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
Objects.requireNonNull(predicate);
return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
如您所见,它返回一个StatelessOp
对象,该对象基本上是一个新的ReferencePipeline,现在已“启用”过滤器评估。 换句话说:每次我们向流中添加新的“功能”时,它都会基于旧的流水线并使用适当的操作标志/方法替代来创建新的流水线。
您可能已经知道,只有在调用急切操作之前,流才会被评估。 因此,我们需要一种急切的方法来评估流。
示例二: forEach(Consumer<?>)
方法:
@Override
public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
刚开始时这很短, evaluate()
方法只不过是调用invoke()
方法而已。 在这里,重要的是要了解ForEachOps.makeRef()
作用。 它设置最后一个必要的标志,并创建一个与ForkJoinTask
对象完全相同的ForEachTask<>
。 令人高兴的是, 安德鲁(Andrew)找到了一篇有关它们如何工作的不错的论文 。
注意:确切的源代码可以在这里找到。
您可能已经知道,Stream API使用Spliterator
和ForkJoinPool
来执行并行计算。 Spliterator
器用于遍历和划分元素序列,而ForkJoinPool
框架将任务递归地分成较小的独立子任务,直到它们足够简单以至于可以异步执行。
作为并行计算框架(例如java.util.stream
包)如何在并行计算java.util.stream
使用Spliterator
和ForkJoinPool
Spliterator
,这是一种实现关联的并行forEach
,该方法说明了主要用法:
public static void main(String[] args) {
List<Integer> list = new SplittableRandom()
.ints(24, 0, 100)
.boxed().collect(Collectors.toList());
parallelEach(list, System.out::println);
}
static <T> void parallelEach(Collection<T> c, Consumer<T> action) {
Spliterator<T> s = c.spliterator();
long batchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8);
new ParallelEach(null, s, action, batchSize).invoke(); // invoke the task
}
叉加入任务:
static class ParallelEach<T> extends CountedCompleter<Void> {
final Spliterator<T> spliterator;
final Consumer<T> action;
final long batchSize;
ParallelEach(ParallelEach<T> parent, Spliterator<T> spliterator,
Consumer<T> action, long batchSize) {
super(parent);
this.spliterator = spliterator;
this.action = action;
this.batchSize = batchSize;
}
// The main computation performed by this task
@Override
public void compute() {
Spliterator<T> sub;
while (spliterator.estimateSize() > batchSize &&
(sub = spliterator.trySplit()) != null) {
addToPendingCount(1);
new ParallelEach<>(this, sub, action, batchSize).fork();
}
spliterator.forEachRemaining(action);
propagateCompletion();
}
}
另外,请记住,并行计算不一定总是比顺序计算快,并且您始终可以选择- 何时使用并行流 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.