我有一个使用Apache Spark的Java程序。该计划中最有趣的部分如下:
ng-value
因此,实际上,行(**)会生成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);
}
格式为
RDD
确实是一个很长的地图链。此外,由于需要更新计数,因此行(*)会在每次迭代时强制计算(非惰性)。
我遇到的问题是,我得到的时间复杂度随着每次迭代而线性增加,因此总体上呈二次方式:
我认为这是因为Spark试图“记住”链中的每个RDD,以及容错算法或导致其增长的任何因素。但是,我真的不知道。
我真正想做的是在每次迭代时告诉Spark“崩溃”RDD,以便只将最后一个保存在内存中并继续工作。我认为这应该导致每次迭代的时间不变。这可能吗?还有其他解决方案吗?
谢谢!
答案 0 :(得分:6)
尝试使用rdd.checkpoint。这将把RDD保存到hdfs并清除沿袭。
每次转换RDD时,都会增加谱系,Spark必须跟踪可用内容和必须重新计算的内容。处理DAG是昂贵的,并且大型DAG倾向于非常快地杀死性能。通过“检查点”,您可以指示Spark计算并保存生成的RDD,并丢弃其创建方式的信息。这使得它类似于简单地保存RDD并将其读回,从而最大限度地减少DAG操作。
在旁注中,由于您遇到了此问题,因此最好通过添加union
来了解steps
也会影响RDD的效果,并且由于谱系的方式也可能会引发StackOverflowError
信息是。 See this post
This link有更多详细信息和精美的图表,主题也提到in this SO post。
答案 1 :(得分:3)
这是一个非常有趣的问题,有一些事情需要考虑。
从根本上说,这是一个迭代算法,如果你看一下Spark中的一些不同的迭代机器学习算法,你可以看到一些处理这类问题的方法。
他们中的大多数人在每次迭代时都不会缓存 - 而是他们有一个可配置的缓存间隔。我可能从每10次迭代缓存开始,看看情况如何。
另一个问题变成了沿袭图,你做的每个mapPartitions
都会使图表增长一些。在某些时候,跟踪数据将开始变得越来越昂贵。 checkpoint
允许您让Spark将当前RDD写入持久存储并丢弃沿袭信息。您可以尝试在每隔20次迭代的某个时间间隔执行此操作并查看其结果。
10和20数字只是一些基本的起点,它们取决于为每次迭代计算数据的速度有多慢,您可以使用它们来找到适合您工作的正确调整。
答案 2 :(得分:1)