3

Spark の DataSetに関するいくつかの優れた記事 ( thisthis、およびthis ) を読んだ後、RDD に対する次の DataSet のパフォーマンス上の利点について説明します。

  1. 論理的および物理的な計画の最適化。
  2. 厳密な類型化;
  3. ベクトル化された操作。
  4. 低レベルのメモリ管理。

質問:

  1. Spark の RDD は、物理的な計画も構築し、同じ段階で複数の変換を結合/最適化できます。では、RDD に対する DataSet の利点は何でしょうか?
  2. 最初のリンクから、の例を見ることができますRDD[Person]DataSet には高度な型付けがありますか?
  3. 「ベクトル化された操作」とはどういう意味ですか?
  4. 私が理解しているように、DataSet の低メモリ管理 = 高度なシリアル化。これは、シリアル化可能なオブジェクトのオフヒープ ストレージを意味し、逆シリアル化せずにオブジェクトの 1 つのフィールドのみを読み取ることができます。しかし、持続戦略がある場合はどうでしょうか? IN_MEMORY_ONLYDataSet はどのような場合でもすべてをシリアル化しますか? RDD よりもパフォーマンス上の利点はありますか?
4

1 に答える 1

6

Spark の RDD は、物理的な計画も構築し、同じ段階で複数の変換を結合/最適化できます。RDD に対する DataSet の利点は何ですか?

RDD を使用する場合、作成したものが得られます。特定の変換は連鎖によって最適化されますが、実行計画は DAG の直接変換です。例えば:

rdd.mapPartitions(f).mapPartitions(g).mapPartitions(h).shuffle()

ここshuffleで、 は任意のシャッフル変換 ( *byKeyrepartitionなど) です。3 つすべてmapPartitions( mapflatMapfilter) は、中間オブジェクトを作成せずにチェーンされますが、再配置することはできません。

それと比較して、Datasetsはるかに制限的なプログラミング モデルを使用しますが、次のような多くの手法を使用して実行を最適化できます。

  • 選択 ( filter) プッシュダウン。たとえば、次の場合:

    df.withColumn("foo", col("bar") + 1).where(col("bar").isNotNull())
    

    次のように実行できます。

    df.where(col("bar").isNotNull()).withColumn("foo", col("bar") + 1)
    
  • 初期の予測 ( select) と削除。例えば:

    df.withColumn("foo", col("bar") + 1).select("foo", "bar")
    

    次のように書き換えることができます。

    df.select("foo", "bar").withColumn("foo", col("bar") + 1)
    

    古いデータのフェッチと受け渡しを避けるため。極端な場合、特定の変換を完全に排除できます。

    df.withColumn("foo", col("bar") + 1).select("bar")
    

    に最適化できます

    df.select("bar")
    

これらの最適化は、次の 2 つの理由で可能です。

  • 複雑で信頼性の低い静的コード分析なしで依存関係分析を可能にする制限付きデータ モデル。
  • 明確な演算子のセマンティクス。演算子には副作用がなく、決定論的なものと非決定論的なものを明確に区別しています。

明確にするために、次のデータ モデルがあるとします。

case class Person(name: String, surname: String, age: Int)

val people: RDD[Person] = ???

そして、21 歳以上のすべての人の姓を取得したいと考えています。これRDDを次のように表現できます。

people
  .map(p => (p.surname, p.age))          // f
  .filter { case (_, age) => age > 21 }  // g

ここで、いくつかの質問を自問してみましょう。

  • ageの入力と変数fの関係は何ですか?ageg
  • and thenはand fthengと同じですか?gf
  • fg副作用は無料ですか?

答えは人間の読者にとっては明らかですが、仮想のオプティマイザーにとってはそうではありません。Dataframeバージョンとの比較:

people.toDF
  .select(col("surname"), col("age"))    // f'
  .where(col("age") > 21)                // g'

その答えは、オプティマイザーと人間の読者の両方にとって明らかです。

これは、静的に型付けされたものDatasets( Spark 2.0 Dataset と DataFrame ) を使用する場合に、さらにいくつかの結果をもたらします。

DataSet はより高度な型付けを取得しましたか?

  • いいえ - 最適化を気にする場合。最も高度な最適化は制限されてDataset[Row]おり、現時点では複雑な型階層をエンコードすることはできません。
  • おそらく - Kryo または Java エンコーダーのオーバーヘッドを受け入れる場合。

「ベクトル化された操作」とはどういう意味ですか?

最適化のコンテキストでは、通常、ループのベクトル化/ループの展開を意味します。Spark SQL はコード生成を使用して、ベクトル化された命令セットを利用するためにさらに最適化できる高レベルの変換のコンパイラーに適したバージョンを作成します。

私が理解しているように、DataSet の低メモリ管理 = 高度なシリアル化。

ではない正確に。ネイティブ割り当てを使用する最大の利点は、ガベージ コレクター ループを回避できることです。ガベージ コレクションは Spark の制限要因であることが非常に多いため、特に大きなデータ構造を必要とするコンテキスト (シャッフルの準備など) では、これは大きな改善です。

もう 1 つの重要な側面は、効率的な圧縮 (潜在的にメモリ フットプリントの削減) と圧縮データに対する最適化された操作を可能にする列型ストレージです。

一般に、plain で手作りのコードを使用して、まったく同じタイプの最適化を適用できますRDDs。結局のところDatasets、によって支えられていRDDsます。違いは、どれだけの労力がかかるかだけです。

  • 手作りの実行計画の最適化は、比較的簡単に実現できます。
  • コードをコンパイラに適したものにするには、より深い知識が必要であり、エラーが発生しやすく、冗長です。
  • sun.misc.Unsafeネイティブのメモリ割り当てを使用することは、気弱な人向けではありません。

そのすべてのメリットにもかかわらず、DatasetAPI は普遍的ではありません。特定のタイプの一般的なタスクは、多くのコンテキストでその最適化の恩恵を受けることができますが、RDD の同等物と比較して、まったく改善されないか、パフォーマンスが低下することさえあります。

于 2016-12-26T16:50:27.217 に答える