JavaApacheSpark:长转换链导致二次时间
我有一个使用ApacheSpark的Java程序。该计划最有趣的部分如下所示:JavaApacheSpark:长转换链导致二次时间,java,apache-spark,Java,Apache Spark,我有一个使用ApacheSpark的Java程序。该计划最有趣的部分如下所示: long seed = System.nanoTime(); JavaRDD<AnnotatedDocument> annotated = documents .mapPartitionsWithIndex(new InitialAnnotater(seed), true); annotated.cache(); for (int iter = 0; iter < 2000; iter+
long seed = System.nanoTime();
JavaRDD<AnnotatedDocument> annotated = documents
.mapPartitionsWithIndex(new InitialAnnotater(seed), true);
annotated.cache();
for (int iter = 0; iter < 2000; iter++) {
GlobalCounts counts = annotated
.mapPartitions(new GlobalCounter())
.reduce((a, b) -> a.sum(b)); // update overall counts (*)
seed = System.nanoTime();
// copy overall counts which CountChanger uses to compute a stochastic thing (**)
annotated = annotated
.mapPartitionsWithIndex(new CountChanger(counts, seed), true);
annotated.cache();
// adding these lines causes constant time complexity like i want
//List<AnnotatedDocument> ll = annotated.collect();
//annotated = sc.parallelize(ll, 8);
}
一系列很长的地图。此外,行(*)在每次迭代时强制计算(非惰性),因为计数需要更新
我的问题是,我得到的时间复杂度随着每次迭代线性增加,因此总体上是二次的:
我认为这是因为Spark试图“记住”链中的每一个RDD,以及容错算法或其他导致RDD增长的因素。然而,我真的不知道
我真正想做的是在每次迭代中告诉Spark“折叠”RDD,这样只有最后一个RDD保留在内存中并继续工作。我认为,这将导致每次迭代的时间恒定。这可能吗?还有其他解决办法吗
谢谢 尝试使用rdd.checkpoint。这将把RDD保存到hdfs并清除沿袭 每次转换RDD时,您都会增加沿袭,Spark必须跟踪哪些可用,哪些需要重新计算。处理DAG非常昂贵,而大型DAG往往会很快降低性能。通过“检查点”,您指示Spark计算并保存生成的RDD,并丢弃其创建方式的信息。这类似于简单地保存RDD并将其读回,从而最小化DAG操作 另一方面,由于您遇到了这个问题,
union
通过添加步骤也会影响RDD性能,并且由于沿袭信息的方式,还可能抛出stackoverflowerr
有更详细的图表,主题也被提及。
< P>这是一个非常有趣的问题,还有一些事情要考虑。
从根本上说,这是一个迭代算法,如果你看看Spark中的一些不同的迭代机器学习算法,你可以看到一些处理这类问题的方法
第一件事是,它们中的大多数不会在每次迭代时缓存,而是有一个可配置的缓存间隔。我可能会从每10次迭代缓存一次开始,看看它是如何进行的
另一个问题是沿袭图,您所做的每一个mapPartitions
都会使图增长一点。在某个时刻,跟踪这些数据将变得越来越昂贵<代码>检查点
允许您让Spark将当前RDD写入持久存储并丢弃沿袭信息。你可以试着每隔20次迭代做一次,看看这是怎么回事
10和20数字只是一种基本的起点,它们取决于计算每个迭代的数据的速度有多慢,您可以使用它们来找到适合您工作的正确调整。- 在每隔几次(需要调整)迭代使用annotated.count()缓存之前,尝试具体化rdd
- 最好使用persist(…)来控制rdd的缓存位置,而不是将rdd放入内存的cache(),persist允许您选择它的缓存位置(取决于内存可用性)
- 最好是“保存”缓存/持久化rdd,然后在缓存/持久化下一个周期后取消持久化。Spark自己做,但是如果您控制它,Spark就不需要选择从缓存中抛出哪个rdd
documents
.mapPartitionsWithIndex(initial)
.mapPartitionsWithIndex(nextIter)
.mapPartitionsWithIndex(nextIter)
.mapPartitionsWithIndex(nextIter)
... 2000 more