Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaApacheSpark:长转换链导致二次时间_Java_Apache Spark - Fatal编程技术网

JavaApacheSpark:长转换链导致二次时间

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+

我有一个使用ApacheSpark的Java程序。该计划最有趣的部分如下所示:

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

是否有任何理由为每次迭代缓存RDD?而不是在循环结束时缓存最后一个累积的RDD?我仍然在试验缓存的效果,所以我的答案应该是“不太可能”。你真的在每次计算中都重用RDD吗?或者每次计算计数器时都是一个新的RDD?更像是一个经过2000次更改的初始RDD。我认为Spark试图在每次迭代中记住链,而我希望它认为,在每次迭代中,RDD都是“新鲜的”。我编辑我的代码是为了澄清这一点。尽量不要缓存RDD,因为每次都在有效地迭代不同的RDD。另外,我建议您查看Spark UI,看看是什么花了这么长时间。也许你的工作是造成GC压力的,但是UI肯定会让你对正在发生的事情有更多的了解。你会考虑更新你的答案吗?
documents
    .mapPartitionsWithIndex(initial)
    .mapPartitionsWithIndex(nextIter)
    .mapPartitionsWithIndex(nextIter)
    .mapPartitionsWithIndex(nextIter)
    ... 2000 more