使用Scala和Spark,我有以下结构:
val rdd1: RDD[String] = ...
val rdd2: RDD[(String, Any)] = ...
val rdd1pairs = rdd1.map(s => (s, s))
val result = rdd2.join(rdd1pairs)
.map { case (_: String, (e: Any, _)) => e }
将rdd1
映射到PairRDD
的目的是在后续步骤中与rdd2
的联接。但是,我实际上只对rdd2
的值感兴趣,因此最后一行中的映射步骤省略了键。实际上,出于效率原因,这是使用Spark rdd2
执行的rdd1
和join()
之间的交集。
我的问题是指rdd1pairs
的键:它们是出于语法原因而创建的(允许连接)在第一个映射步骤中,后来被丢弃而没有任何用法。编译器如何处理这个?在内存消耗方面是否重要,我是否使用字符串s
(如示例所示)?我应该用null
或0
替换它以节省一点内存吗?编译器是否实际创建并存储了这些对象(引用),还是注意到它们从未被使用过?
答案 0 :(得分:3)
在这种情况下,我认为Spark驱动程序将会影响结果,而不是编译器。 Spark是否可以优化其执行管道,以避免创建s
的冗余重复。我不确定,但我认为Spark会在内存中创建rdd1pairs
。
您可以使用(String, String)
:
(String, Unit)
rdd1.map(s => (s,()))
您正在做的事情基本上是基于rdd2
的{{1}}过滤器。如果rdd1明显小于rdd2,则另一种方法是将rdd1
的数据表示为广播变量而不是RDD,并简单地过滤rdd1
。这可以避免任何混乱或减少阶段,因此可能更快,但仅在rdd2
的数据小到足以适合每个节点时才有效。
编辑:
考虑如何使用Unit而不是String节省空间,请考虑以下示例:
rdd1
和
object size extends App {
(1 to 1000000).map(i => ("foo"+i, ()))
val input = readLine("prompt> ")
}
使用此问题How to check heap usage of a running JVM from the command line?中描述的object size extends App {
(1 to 1000000).map(i => ("foo"+i, "foo"+i))
val input = readLine("prompt> ")
}
命令,第一个版本使用的堆比后者少得多。
编辑2:
jstat
实际上是一个没有内容的单例对象,因此从逻辑上讲,它不需要任何序列化。类型定义包含Unit
这一事实告诉您,您需要能够反序列化具有Unit类型字段的结构。
Spark默认使用Java Serialization。请考虑以下事项:
Unit
这两个文件的大小相同:
object Main extends App {
import java.io.{ObjectOutputStream, FileOutputStream}
case class Foo (a: String, b:String)
case class Bar (a: String, b:String, c: Unit)
val str = "abcdef"
val foo = Foo("abcdef", "xyz")
val bar = Bar("abcdef", "xyz", ())
val fos = new FileOutputStream( "foo.obj" )
val fo = new ObjectOutputStream( fos )
val bos = new FileOutputStream( "bar.obj" )
val bo = new ObjectOutputStream( bos )
fo writeObject foo
bo writeObject bar
}
和
�� sr Main$Foo3�,�z \ L at Ljava/lang/String;L bq ~ xpt abcdeft xyz