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
。为什么不在
过滤器中定义函数呢?或者使用可以广播的
对象