Scala 对于数据集,如何处理新对象实例化?
我必须遵循以下场景Scala 对于数据集,如何处理新对象实例化?,scala,dataframe,apache-spark,Scala,Dataframe,Apache Spark,我必须遵循以下场景 case class A(name:String) class Eq { def isMe(s:String) = s == "ME" } val a = List(A("ME")).toDS a.filter(l => new Eq().isMe(l.name)) 这是否每次为每个执行器上的每个数据点创建一个新对象Eq!我不知道类型化数据集有不同的筛选方法。 为了回答你的问题,我将深入探讨Spark的内部结构 键入的数据集上的过滤器具有以下签名: def f
case class A(name:String)
class Eq { def isMe(s:String) = s == "ME" }
val a = List(A("ME")).toDS
a.filter(l => new Eq().isMe(l.name))
这是否每次为每个执行器上的每个数据点创建一个新对象
Eq
!我不知道类型化数据集有不同的筛选方法。为了回答你的问题,我将深入探讨Spark的内部结构 键入的数据集上的过滤器具有以下签名:
def filter(func: T => Boolean): Dataset[T]
请注意,func
是用T
参数化的,因此Spark需要将对象A
与函数一起反序列化
TypedFilter Main$$$Lambda$, class A, [StructField(name,StringType,true)], newInstance(class A)
其中,Main$$$$Lambda$
是随机生成的函数名
在优化阶段,如果满足以下条件,则该规则可能会消除:
ds.map(…).filter(…)
可以通过此规则进行优化以保存额外的反序列化,但是ds.map(…).as[AnotherType]。无法优化筛选器(…)
如果规则适用TypedFilter
将按
这里的捕获是过滤器的条件
。事实上,它是另一个名为where:targetObject
是过滤函数Main$$$$Lambda$
functionName
是apply
,因为它是一个常规的Scala函数
Spark最终以这两种模式之一运行—生成代码
或解释器
。让我们专注于第一个,因为它是默认值
下面是将生成代码的方法调用的简化堆栈跟踪
SparkPlan.execute
//https://github.com/apache/spark/blob/03e30063127fd71bef8a14553381e805fe5b6679/sql/core/src/main/scala/org/apache/spark/sql/execution/WholeStageCodegenExec.scala#L596
-> WholeStageCodegenExec.execute
[child: Filter]
-> child.execute
[condition Invoke]
-> Invoke.genCode
//https://github.com/apache/spark/blob/branch-2.4/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala#L345
-> doGenCode
生成阶段后的简化代码:
final class GeneratedIteratorForCodegenStage1 extends BufferedRowIterator {
private Object[] references;
private scala.collection.Iterator input;
private UnsafeRowWriter writer = new UnsafeRowWriter();
public GeneratedIteratorForCodegenStage1(Object[] references) {
this.references = references;
}
public void init(Iterator inputs) {
this.inputs = inputs;
}
protected void processNext() throws IOException {
while (input.hasNext() && !stopEarly()) {
InternalRow row = input.next();
do {
//Create A object
UTF8String value = row.getUTF8String(0));
A a = new A(value.toString)
//Filter by A's value
result = (scala.Function1) references[0].apply(a);
if (!result) continue;
writer.write(0, value)
append((writer.getRow());
}
if (shouldStop()) return;
}
}
}
我们可以看到,投影是通过引用变量中传递的对象数组构造的。但是引用变量实例化的位置和次数是多少?它是在批发性tagecodegenexec期间创建的,并且每次仅实例化一次 这就引出了一个答案,
filter
函数将只在每个分区创建一次,而不是在每个数据点创建一次,Eq
和A
类将在每个数据点创建
如果您对它添加到代码上下文的位置感到好奇:发生了 其中
javaType
是scala.function1
。
value
是实现-Main$$$$Lambda$
是,对于每个记录,都将实例化一个Eq
。为什么不在过滤器中定义函数呢?或者使用可以广播的对象