我试图坚持一个火花RDD,其中每个分区的元素都共享一个大对象的访问权限。但是,此对象似乎多次存储在内存中。将我的问题减少到仅有200个元素的单个分区的玩具箱:
val nElements = 200
class Elem(val s:Array[Int])
val rdd = sc.parallelize(Seq(1)).mapPartitions( _ => {
val sharedArray = Array.ofDim[Int](10000000) // Should require ~40MB
(1 to nElements).toIterator.map(i => new Elem(sharedArray))
}).cache()
rdd.count() //force computation
这会消耗预期的内存量,如日志中所示:
storage.MemoryStore:阻止rdd_1_0存储为内存中的值(估计大小为38.2 MB,空闲5.7 GB)
但是,200是这样的元素的最大数量。设置nElements=201
会产生:
storage.MemoryStore:阻止rdd_1_0存储为内存中的值(估计大小为76.7 MB,空闲5.7 GB)
是什么原因引起的?这个神奇的数字200来自哪里,我该怎样才能增加它?
编辑澄清:
向函数添加println表明它确实只调用一次。此外,运行:
rdd.map(_.s.hashCode).min == rdd.map(_.s.hashCode).max // returns true
..揭示了所有10000000个元素确实指向同一个对象,因此数据结构本质上表现正确。当nExamples更大(例如20000)时会出现问题,因此它无法持久存在。
storage.MemoryStore:没有足够的空间来缓存内存中的rdd_1_0! (到目前为止计算为6.1 GB)
当我设置nExamples=500
时,它成功地将rdd保留在内存中,说估计大小为1907.4 MB ,但我可以看到我的内存使用量的实际增加远远小于此值。< / p>
答案 0 :(得分:0)
对于任何在未来遇到这种情况的人来说,我最终想出了一个超级黑客的解决方案(尽管我仍然乐于听到更好的解决方案)。而不是使用rdd.cache(),我定义:
def cached[T: ClassTag](rdd:RDD[T]) = {
rdd.mapPartitions(p =>
Iterator(p.toSeq)
).cache().mapPartitions(p =>
p.next().toIterator
)
}
以便cached(rdd)
返回从缓存的&#39;生成的RDD。列表