Каков правильный способ статического объекта для всех работников

Я смотрел документацию на искру, и он упоминает это:

API Sparks в значительной степени зависит от передачи функций в программе драйвера для запуска в кластере. Существует два рекомендуемых способа:

Анонимный синтаксис функций, который можно использовать для коротких фрагментов кода. Статические методы в глобальном одноэлементном объекте. Например, вы можете определить объект MyFunctions, а затем передать MyFunctions.func1 следующим образом:

object MyFunctions {   def func1(s: String): String = { ... } }

myRdd.map(MyFunctions.func1) 

Обратите внимание, что, хотя также возможно передать ссылку на метод в экземпляре класса (в отличие от singleton object), это требует отправки объекта, который содержит класса вместе с методом. Например, рассмотрим:

class MyClass {   
  def func1(s: String): String = { ... }   
  def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(func1) } 
} 

Здесь, если мы создадим новый MyClass и назовите doStuff на нем, карта внутри ссылается на метод func1 этого экземпляра MyClass, поэтому весь объект должен быть отправлен в кластер. Это похоже на запись rdd.map(x => this.func1(x)).

Теперь мое сомнение в том, что произойдет, если у вас есть атрибуты для объекта singleton (которые должны быть эквивалентны статическому). Тот же пример с небольшим изменением:

object MyClass {   
  val value = 1   
  def func1(s: String): String = { s + value }   
} 

myRdd.map(MyClass.func1) 

Таким образом, функция все еще ссылается статически, но насколько далеко продвигается Spark, пытаясь сериализовать все ссылочные переменные? Будет ли он сериализоваться value или он будет снова инициализирован удаленными сотрудниками?

Кроме того, это все в контексте, что у меня есть некоторые тяжелые модели внутри одного объекта, и я хотел бы найти правильный способ сериализации их для рабочих, сохраняя возможность ссылаться на них из синглтона повсюду, а не на передачу их вокруг как функциональные параметры в довольно глубоком стеке вызовов функций.

Любая подробная информация о том, что/как/когда делает сериализацию Spark, будет оценена.

Ответ 1

Это меньше вопрос о Spark и многое другое о том, как Scala генерирует код. Помните, что Scala object в значительной степени является классом Java, полным статических методов. Рассмотрим простой пример:

object foo {

  val value = 42

  def func(i: Int): Int = i + value

  def main(args: Array[String]): Unit = {
    println(Seq(1, 2, 3).map(func).sum)
  }

}

Это будет переведено на 3 класса Java; одним из них будет замыкание, являющееся параметром метода map. Использование javap в этом классе дает что-то вроде этого:

public final class foo$$anonfun$main$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
  public static final long serialVersionUID;
  public final int apply(int);
  public int apply$mcII$sp(int);
  public final java.lang.Object apply(java.lang.Object);
  public foo$$anonfun$main$1();
}

Обратите внимание, что полей нет. Если вы посмотрите на дизассемблированный байт-код, все, что он делает, это вызов метода func(). При запуске в Spark это экземпляр, который будет сериализован; поскольку он не имеет полей, сериализации не так много.

Что касается вашего вопроса, как инициализировать статические объекты, вы можете иметь идемпотентную функцию инициализации, которую вы вызываете в начале ваших закрытий. Первый инициирует инициализацию, последующие вызовы будут не-ops. Очистка, однако, намного сложнее, поскольку я не знаком с API, который делает что-то вроде "запускает этот код для всех исполнителей".

Один подход, который может быть полезен, если вам требуется очистка, объясняется в этом блоге в разделе "setup() и cleanup()".

EDIT: просто для пояснения, здесь разбор метода, который фактически выполняет вызов.

public int apply$mcII$sp(int);
  Code:
   0:   getstatic       #29; //Field foo$.MODULE$:Lfoo$;
   3:   iload_1
   4:   invokevirtual   #32; //Method foo$.func:(I)I
   7:   ireturn

Посмотрите, как он просто ссылается на статическое поле, удерживающее синглтон, и вызывает метод func().