(Scala特定问题。)
尽管Spark文档鼓励尽可能使用DataFrame API,但如果DataFrame API不足,则通常选择回退到RDD API或使用UDF。这两种选择之间是否存在固有的性能差异?
RDD和UDF类似,因为它们都不能从Catalyst和Tungsten优化中受益。是否还有其他开销,如果存在,两种方法之间是否存在差异?
举一个具体的例子,让我说我有一个DataFrame,其中包含一列带有自定义格式的文本数据(不适合regexp匹配)。我需要解析该列并添加一个包含结果标记的新向量列。
答案 0 :(得分:11)
它们都不能从Catalyst和Tungsten优化中受益
这不完全正确。虽然UDF不会从Tungsten优化中受益(可以说简单的SQL转换也不会得到巨大的推动),但您仍然可以从Catalyst提供的执行计划优化中受益。让我们举一个简单的例子来说明(注意:Spark 2.0和Scala。不要将它推断到早期版本,尤其是使用PySpark):
val f = udf((x: String) => x == "a")
val g = udf((x: Int) => x + 1)
val df = Seq(("a", 1), ("b", 2)).toDF
df
.groupBy($"_1")
.agg(sum($"_2").as("_2"))
.where(f($"_1"))
.withColumn("_2", g($"_2"))
.select($"_1")
.explain
// == Physical Plan ==
// *HashAggregate(keys=[_1#2], functions=[])
// +- Exchange hashpartitioning(_1#2, 200)
// +- *HashAggregate(keys=[_1#2], functions=[])
// +- *Project [_1#2]
// +- *Filter UDF(_1#2)
// +- LocalTableScan [_1#2, _2#3]
执行计划向我们展示了几件事:
根据数据和管道的不同,这几乎可以免费提供显着的性能提升。
据说,RDD和UDF都要求安全和不安全之间的迁移,后者的灵活性要低得多。尽管如此,如果你需要的只是一个简单的map
- 类似的行为而没有初始化昂贵的对象(比如数据库连接),那么UDF就是你的选择。
在稍微复杂的情况下,您可以轻松下载到通用Dataset
并保留RDDs
,以便在您真正需要访问某些低级功能(如自定义分区)时使用。
答案 1 :(得分:0)
(注意:我没有为此提供测量支持)
对我来说,shuffle 和(反)序列化是主要的成本。但在这些之后,拥有干净的代码才是最重要的。考虑到这一点:
使用 RDD 操作的主要缺点是需要(反)序列化/成完整的 jvm 对象。虽然使用 udf 可能只会(反)序列化所需的列。请注意,这是在处理面向列的数据(例如镶木地板)时,对于我不知道的其他数据格式,但希望在许多情况下两者具有相似的性能。
因此,如果您的算法主要是过滤和改组操作,和/或可以简单地用数据帧操作和本地 udf 表示,您应该使用它们。但是,如果您的算法需要对多列进行复杂处理,最好预先进行反序列化,并在 jvm 对象上执行干净高效的 Scala 代码。
因此,根据我实现复杂数学算法的个人经验,我通常将代码分为两步: