Apache Spark:マップvs mapPartitions?


133

RDD mapmapPartitionsメソッド違いは何ですか?そして、のようにflatMap動作しますか?ありがとう。mapmapPartitions

(編集)つまり、(意味的にまたは実行に関して)違いは何ですか

  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 /…
Abhidemon

回答:


121

RDDのマップとmapPartitionsメソッドの違いは何ですか?

メソッドマップは、関数を適用することにより、ソースRDDの各要素を結果RDDの単一の要素に変換します。mapPartitionsは、ソースRDDの各パーティションを結果の複数の要素に変換します(おそらくなし)。

また、flatMapはmapまたはmapPartitionsのように動作しますか?

また、flatMapは単一の要素(としてmap)で機能し、結果の複数の要素を(としてmapPartitions)生成します。


3
おかげで-マップはシャッフルを引き起こしますか(またはパーティションの数を変更します)?ノード間でデータを移動しますか?ノード間でのデータの移動を回避するためにmapPartitionsを使用してきましたが、flapMapがそうなるかどうかはわかりませんでした。
ニコラスホワイト

-あなたは、ソースを見ればgithub.com/apache/incubator-spark/blob/...github.com/apache/incubator-spark/blob/... -の両方mapflatMap親と全く同じパーティションを持っています。
Alexey Romanov 2014年

13
注として、2013年のサンフランシスコ火花サミット(goo.gl/JZXDCR)で講演者が提供したプレゼンテーションでは、レコードごとのオーバーヘッドが高いタスクは、mapPartitionの方がマップ変換よりもパフォーマンスが優れていることが強調されています。これは、プレゼンテーションによると、新しいタスクを設定するための高コストによるものです。
Mikel Urkia 14

1
反対のことがわかります-非常に小さな操作でも、mapPartitionsを呼び出してmapを呼び出すよりも高速です。これは、マップタスクを処理する言語エンジンを起動するオーバーヘッドにすぎないと想定しています。(私はRにいるので、起動オーバーヘッドが大きくなる可能性があります。)複数の操作を実行する場合、mapPartitionsはかなり高速に見えます。これは、RDDを1回しか読み取らないためです。RDDがRAMにキャッシュされている場合でも、型変換によるオーバーヘッドを大幅に節約できます。
ボブ

3
map基本的には関数を受け取り、fそれをに渡しますiter.map(f)。したがって、基本的には、ラップする便利なメソッドですmapPartitions。純粋なマップスタイルの変換ジョブ(つまり、機能が同じ場合)のいずれかの方法でパフォーマンス上の利点があった場合は驚きます。処理のためにオブジェクトを作成する必要がある場合、これらのオブジェクトを共有できるmapPartitionsと有利です。
NightWolf、2015

129

インプ。ヒント :

RDD要素ごとに1回ではなく、多くの要素に対して1回実行する必要がある重い初期化があるRDD場合、およびサードパーティライブラリからのオブジェクトの作成などの初期化をシリアル化できない場合(Sparkがクラスター全体でそれを送信できるようにするため)ワーカーノード)のmapPartitions()代わりに 使用しますmap()たとえば、データ要素mapPartitions()ごとに1回ではなく、ワーカータスク/スレッド/パーティションごとに1回実行される初期化を提供します。以下を参照してください。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の違いは何mapmapPartitions

mapmapPartitionsパーティションレベルで機能を実行しながら、要素ごとのレベルで使用されて いる機能を動作させます。

シナリオ例 特定のRDDパーティションに100Kの要素がある場合、マッピング変換によって使用されている関数を、使用時に100K回起動しますmap

逆に、使用する場合mapPartitions、特定の関数を1回だけ呼び出しますが、すべての100Kレコードを渡して、1回の関数呼び出しですべての応答を返します。

map特定の関数を何度も処理するため、特にすべての要素を一度に渡した場合に実行する必要がないほど、その関数が毎回高価な処理を実行している場合は、パフォーマンスが向上します(の場合mappartitions)。

地図

RDDの各アイテムに変換関数を適用し、結果を新しいRDDとして返します。

バリアントの一覧表示

def map [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

これは、パーティションごとに1回だけ呼び出される特殊なマップです。各パーティションのコンテンツ全体は、入力引数(Iterarator [T])を介して値の順次ストリームとして使用できます。カスタム関数はさらに別のIterator [U]を返す必要があります。結合された結果イテレータは自動的に新しいRDDに変換されます。選択したパーティショニングにより、タプル(3,4)と(6,7)が次の結果から欠落していることに注意してください。

preservesPartitioning入力関数がパーティショナーを保持するかどうかを示しfalseます。これはペアのRDDであり、入力関数がキーを変更しない場合を除きます。

バリアントの一覧表示

def mapPartitions [U:ClassTag](f:Iterator [T] => Iterator [U]、preservesPartitioning:Boolean = false):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、関数を1回/要素ではなく1回/パーティションで呼び出すため、変換よりも高速です。

参考資料foreachとforeachPartitionsの比較


4
私はあなたが同じ結果を使用しmapたりmapPartitions達成したりできることを知っています(質問の2つの例を参照)。この質問は、なぜ他の方法を選ぶのかについてです。他の回答のコメントは本当に役に立ちます!また、あなたはそれについて言及せずmap、にflatMap渡しfalseましたがpreservesPartitioning、それが意味するところは何ですか。
ニコラスホワイト

2
毎回実行される関数と、パーティションに対して1回実行される関数は、私が見逃していたリンクでした。mapPartitionを使用して一度に複数のデータレコードにアクセスできることは非常に重要です。答えに感謝
セミコロンとダクトテープ

1
mapより良いシナリオはありmapPartitionsますか?もしそれmapPartitionsがとても良ければ、なぜそれがデフォルトのマップ実装ではないのですか?
ruhong

1
@oneleggedmule:db接続のようなリソースをインスタンス化する場合(上記の例に示されているように)コストがかかり、mappartitionsはパーティションごとに1つの接続があるため、適切なアプローチである場合、どちらも適切に使用する必要があるさまざまな要件に対応します。また、saveAsTextFile内部で使用されるマップパーティションを 参照
Ram Ghadiyaram

@oneleggedmule私の観点から見ると、map()は理解と学習が容易であり、多くの異なる言語の一般的な方法でもあります。最初にこのSpark固有のメソッドに慣れていない場合は、mapPartitions()よりも使いやすいかもしれません。パフォーマンスに違いがない場合は、map()を使用することをお勧めします。
Raymond Chen

15

地図

  1. MapReduceのmap()メソッドとよく似ており、一度に1行ずつ処理します。
  2. 行ごとに変換から戻ります。

MapPartitions

  1. パーティション全体を一度に処理します。
  2. 関数から戻ることができるのは、パーティション全体を処理した後で一度だけです。
  3. パーティション全体を処理するまで、すべての中間結果をメモリに保持する必要があります。
  4. MapReduceのようなsetup()map()およびcleanup()関数を提供します

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 2017年

4
パーティション全体をメモリに保持する必要はありませんが、結果は保持されます。パーティション全体を処理するまで結果を返すことはできません
KrazyGautam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.