UDF无法从哪些优化中受益?

时间:2019-04-22 08:10:29

标签: apache-spark apache-spark-sql catalyst-optimizer

Spark UDF包含以下功能:可为空,确定性,数据类型等。因此,根据此信息,它将受益于诸如ConstantFolding之类的优化。它可以从哪些其他优化中受益,而不能从哪些优化中受益?我之所以这么问,是因为许多演示文稿将UDF呈现为黑匣子,这没有从催化剂优化中受益,但是显然,它受益于ConstantFolding。

1 个答案:

答案 0 :(得分:0)

Spark通过将UDF包装在类内来处理UDF。例如,当您编写以下代码时:

val example = udf((a: Int) => a * 2)

udf函数的作用是创建一个UserDefinedFunction类,该类在其apply函数中创建一个ScalaUDF. ScalaUDF扩展Expression,并在其doCodeGen方法中执行以下操作:

...
    val callFunc =
      s"""
         |$boxedType $resultTerm = null;
         |try {
         |  $resultTerm = ($boxedType)$resultConverter.apply($getFuncResult);
         |} catch (Exception e) {
         |  throw new org.apache.spark.SparkException($errorMsgTerm, e);
         |}
       """.stripMargin

    ev.copy(code =
      code"""
         |$evalCode
         |${initArgs.mkString("\n")}
         |$callFunc
...

此函数将列/表达式的DataType转换为Scala类型(因为UDF对scala类型进行操作),然后调用lambda。 deterministic, nullable,dataTypes是用户定义函数的包装器的函数,因为它扩展了Expression,而不是您的函数。如果您想从中受益,则必须编写一个自定义Expression,扩展Expression或其子类之一。

以以下示例为例:

val redundantUdf = udf((a: Long) => true)
someDf.filter(redundantUdf(someDf("col1"))).explain()

优化的逻辑计划应如下所示:

Project [_1#5736 AS Type#5739, _2#5737L AS sts#5740L]
 +- Filter UDF(_2#5737L)
  +- LocalRelation [_1#5736, _2#5737L]

如您所见,即使它是多余的并且仍将始终为true,它仍在进行过滤。

以下内容:

someDf.filter(expr("true")).explain()

将给出以下优化的逻辑计划:

LocalRelation [Type#5739, sts#5740L]

它使用PruneFilter规则修剪过滤器。

这并不意味着所有优化都被排除在外,有些优化仍适用于UDF,例如CombineFilter,它结合了两个过滤器的表达式,例如:

== Analyzed Logical Plan ==
_1: string, _2: string
Filter UDF(_1#2)
+- Filter UDF(_1#2)
   +- LocalRelation [_1#2, _2#3]

== Optimized Logical Plan ==
Filter (UDF(_1#2) && UDF(_1#2))
+- LocalRelation [_1#2, _2#3]

此优化之所以有效,是因为它仅取决于deterministic字段,并且默认情况下UDF是确定性的。因此,UDF将受益于不依赖于其包装功能的简单优化。这是因为它采用的格式是催化剂无法理解的,催化剂在Trees上运行,并且您的闭包是Scala函数。 UDF在其他地方会丢失,例如指定生成的Java代码和Spark类型信息。