Apache Spark: карта проти картиПаделі?


133

Яка різниця між RDD map та mapPartitionsметодом? А чи flatMapповодиться так mapчи як mapPartitions? Дякую.

(редагувати), тобто яка різниця (семантично чи з точки зору виконання) між

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.mapPartitions({ iter: Iterator[A] => for (i <- iter) yield fn(i) },
      preservesPartitioning = true)
  }

І:

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.map(fn)
  }

3
Прочитавши нижченаведену відповідь, ви можете ознайомитись із [цим досвідом], яким поділився той, хто насправді ним користувався. ( Bzhangusc.wordpress.com/2014/06/19/… ) bzhangusc.wordpress.com/2014/06/19 /…
Абхідгемон

Відповіді:


121

Чим відрізняється карта RDD від методу mapPartitions?

Метод карта перетворює кожен елемент вихідного RDD в єдиний елемент результату RDD шляхом застосування функції. mapPartitions перетворює кожен розділ вихідного RDD в кілька елементів результату (можливо, жодного).

І чи поводиться flatMap як карта чи як mapPartitions?

Жоден з них, flatMap не працює над одним елементом (як map) і створює декілька елементів результату (як mapPartitions).


3
Дякую - так чи карта викликає перетасування (чи іншим чином змінить кількість розділів)? Чи переміщує дані між вузлами? Я використовував mapPartitions, щоб уникнути переміщення даних між вузлами, але не був впевнений, чи зробить це flapMap.
Микола Білий

Якщо ви подивитесь на джерело - github.com/apache/incubator-spark/blob/… та github.com/apache/incubator-spark/blob/…, обидва mapі flatMapмають точно такі ж розділи, як і батьків.
Олексій Романов

13
Як зауваження, в презентації, яку виголосив доповідач на Саміті Сан-Франциско Спарк у 2013 році (goo.gl/JZXDCR), підкреслюється, що завдання з високими накладними накладними витратами виконуються краще за допомогою MapPartition, ніж при трансформації карти. Це, згідно з презентацією, пов’язано з високою вартістю постановки нового завдання.
Мікель Уркія

1
Я бачу навпаки - навіть при дуже малих операціях швидше викликати mapPartitions і ітерацію, ніж карту викликів. Я припускаю, що це лише накладні витрати на запуск мовного механізму, який буде обробляти завдання з картою. (Я в R, який може мати більше запуску накладних витрат.) Якщо ви виконували б кілька операцій, то mapPartitions здається зовсім трохи швидшим - я припускаю, що це відбувається тому, що він читає RDD лише один раз. Навіть якщо RDD кешується в оперативній пам'яті, це економить великі накладні витрати від перетворення типів.
Боб

3
mapв основному приймає вашу функцію fі передає її в iter.map(f). Так в основному це зручний метод, який завершує mapPartitions. Я був би здивований, якби для будь-якої роботи з перетворення чистого стилю карти (наприклад, де функція ідентична) була якась перевага в продуктивності, якщо вам потрібно створити якісь об'єкти для обробки, якщо ці об’єкти можна спільно використовувати, то mapPartitionsбуло б вигідно.
NightWolf

129

Імп. ПОРАДА :

Кожен раз, коли у вас є ініціалізація важкої ваги, яку слід робити один раз для багатьох RDDелементів, а не один раз на RDDелемент, і якщо ця ініціалізація, наприклад створення об'єктів із сторонньої бібліотеки, не може бути серіалізована (щоб Spark міг передати її через кластер до робочі вузли), використовувати mapPartitions()замість map(). mapPartitions()передбачає, що ініціалізація повинна бути виконана один раз для кожного робочого завдання / потоку / розділу замість одного разу на RDDелемент даних, наприклад: див. нижче.

val newRd = myRdd.mapPartitions(partition => {
  val connection = new DbConnection /*creates a db connection per partition*/

  val newPartition = partition.map(record => {
    readMatchingFromDB(record, connection)
  }).toList // consumes the iterator, thus calls readMatchingFromDB 

  connection.close() // close dbconnection here
  newPartition.iterator // create a new iterator
})

Q2. чи flatMapповодиться як карта чи як mapPartitions?

Так. дивіться, будь ласка, приклад 2 flatmap.. її пояснення.

Q1. Яка різниця між RDD mapтаmapPartitions

mapпрацює функція, що використовується на рівні елемента, в той час як mapPartitionsвиконує функцію на рівні розділу.

Приклад Сценарій : якщо в конкретномуRDDрозділіу нас є 100K елементів,тоді ми знімемо функцію, яку використовує трансформація відображення 100K разів, коли ми використовуємоmap.

І навпаки, якщо ми використовуємо, mapPartitionsто ми будемо викликати певну функцію лише один раз, але ми передамо всі записи в 100 К і отримаємо всі відповіді за один виклик функції.

Збільшується продуктивність, оскільки mapпрацює над певною функцією стільки разів, особливо якщо функція робить що-небудь дороге, що не потрібно було б робити, якщо ми передамо всі елементи одразу (у випадку mappartitions).

карта

Застосовує функцію перетворення до кожного елемента RDD і повертає результат як новий RDD.

Варіанти лістингу

визначити карту [U: ClassTag] (f: T => U): RDD [U]

Приклад:

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
 val b = a.map(_.length)
 val c = a.zip(b)
 c.collect
 res0: Array[(String, Int)] = Array((dog,3), (salmon,6), (salmon,6), (rat,3), (elephant,8)) 

mapPartitions

Це спеціалізована карта, яка викликається лише один раз для кожного розділу. Весь вміст відповідних розділів доступний у вигляді послідовного потоку значень через вхідний аргумент (Iterarator [T]). Спеціальна функція повинна повертати ще один ітератор [U]. Об'єднані ітератори результатів автоматично перетворюються на новий RDD. Зверніть увагу, що кортежі (3,4) та (6,7) відсутні в наступному результаті через обраний нами розділ.

preservesPartitioningвказує, чи зберігає функція введення частку, яка повинна бути, falseякщо це не пара RDD і функція введення не змінює клавіші.

Варіанти лістингу

def mapPartitions [U: ClassTag] (f: Ітератор [T] => Ітератор [U], зберігаєПереділення: Булева = хибна): RDD [U]

Приклад 1

val a = sc.parallelize(1 to 9, 3)
 def myfunc[T](iter: Iterator[T]) : Iterator[(T, T)] = {
   var res = List[(T, T)]()
   var pre = iter.next
   while (iter.hasNext)
   {
     val cur = iter.next;
     res .::= (pre, cur)
     pre = cur;
   }
   res.iterator
 }
 a.mapPartitions(myfunc).collect
 res0: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8)) 

Приклад 2

val x = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10), 3)
 def myfunc(iter: Iterator[Int]) : Iterator[Int] = {
   var res = List[Int]()
   while (iter.hasNext) {
     val cur = iter.next;
     res = res ::: List.fill(scala.util.Random.nextInt(10))(cur)
   }
   res.iterator
 }
 x.mapPartitions(myfunc).collect
 // some of the number are not outputted at all. This is because the random number generated for it is zero.
 res8: Array[Int] = Array(1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 7, 7, 7, 9, 9, 10) 

Вищеописану програму також можна записати за допомогою flatMap наступним чином.

Приклад 2 з використанням плоскої карти

val x  = sc.parallelize(1 to 10, 3)
 x.flatMap(List.fill(scala.util.Random.nextInt(10))(_)).collect

 res1: Array[Int] = Array(1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10) 

Висновок:

mapPartitionsперетворення відбувається швидше, ніж mapоскільки воно викликає вашу функцію один раз / розділ, а не один раз / елемент ..

Подальше читання: foreach Vs foreachPartitions Коли користуватися Що?


4
Я знаю, що ви можете використовувати mapабо mapPartitionsдосягти однакового результату (див. Два приклади у запитанні); це питання стосується того, чому ви обираєте один шлях над іншим. Коментарі в іншій відповіді дуже корисні! Крім того, ви не згадали про це mapі flatMapпереходите falseдо цього preservesPartitioning, і які це наслідки.
Микола Білий

2
функція, що виконується кожного разу проти функції, що виконується один раз для розбиття, була посиланням, яке мені бракувало. Доступ до декількох записів даних одночасно з mapPartition - це безцінна річ. оцініть відповідь
напівколонки та клейка стрічка

1
Чи є сценарій, де mapкраще, ніж mapPartitions? Якщо mapPartitionsце так добре, чому це не реалізація карти за замовчуванням?
ruhong

1
@oneleggedmule: обидва пред'являються до різних вимог, які ми мусимо з розумом використовувати, якщо ви створюєте інстанціювання ресурсів, таких як db-з'єднання (як показано у наведеному вище прикладі), які коштують дорого, а потім розділ map - це правильний підхід, оскільки одне з'єднання на розділ. також saveAsTextFile, що використовується на внутрішніх картах, перегляньте
Ram Ghadiyaram

@oneleggedmule З моєї точки зору, map () простіше зрозуміти та вивчити, а також це звичайний метод для багатьох різних мов. Це може бути і простішим у використанні, ніж mapPartitions (), якщо хтось на початку не знайомий із цим специфічним методом Spark. Якщо різниці в продуктивності немає, я вважаю за краще використовувати map ().
Реймонд Чен

15

Карта :

  1. Він обробляє один рядок, дуже схожий на метод map () MapReduce.
  2. Ви повертаєтесь із трансформації після кожного ряду.

MapPartitions

  1. Він обробляє повний розділ за один раз.
  2. Ви можете повернутися з функції лише один раз після обробки всього розділу.
  3. Всі проміжні результати потрібно зберігати в пам’яті, поки ви не обробите весь розділ.
  4. Надає вам функцію setup () map () та очищення () функції MapReduce

Map Vs mapPartitions http://bytepadding.com/big-data/spark/spark-map-vs-mappartitions/

Spark Map http://bytepadding.com/big-data/spark/spark-map/

Spark mapPartitions http://bytepadding.com/big-data/spark/spark-mappartitions/


щодо 2 - якщо ви здійснюєте перетворення ітератора в ітератор і не матеріалізуєте ітератор до якоїсь колекції, вам не доведеться зберігати весь розділ у пам'яті, насправді таким чином іскра зможе розлийте частини розділу на диск.
ilcord

4
Вам не потрібно зберігати весь розділ в пам'яті, але результат. Ви не можете повернути результат, поки не обробили весь розділ
KrazyGautam,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.