2016-04-13 11 views
9

私はjava-sizeofライブラリ(https://github.com/phatak-dev/java-sizeof)で遊んでいて、Apache Sparkでデータセットのサイズを測定しています。それが判明したので、オブジェクトはばかげて大きいです。大規模なように - それはなぜですか?Spark Rowオブジェクトが同等の構造体に比べて大きすぎるのはなぜですか?

はかなり単純なスキーマを取る:

root 
|-- account: string (nullable = true) 
|-- date: long (nullable = true) 
|-- dialed: string (nullable = true) 
|-- duration: double (nullable = true) 

例のデータは、次のようになります。

+-------+-------------+----------+--------+ 
|account|   date| dialed|duration| 
+-------+-------------+----------+--------+ 
| 5497|1434620384003|9075112643| 790.0| 
+-------+-------------+----------+--------+ 

は、だから今、私たちは行います

val row = df.take(1)(0) 
// row: org.apache.spark.sql.Row = [5497,1434620384003,9075112643,790.0] 

は、だから今はSizeEstimator

を使用します
SizeEstimator.estimate(row) 
// res19: Long = 85050896 

81メガバイト!単一の行の場合!これは間違いのいくつかの種類がされて考えて、私が行います

SizeEstimator.estimate(df.take(100)) 
// res20: Long = 85072696 

興味深いことに、それははるかに大きいではありません - 100倍のデータ量を保持しているにもかかわらず、たったの約20Kより大きく。 100を超えると線形になるようです。 1,000行の場合、次のようになります。

SizeEstimator.estimate(df.take(1000)) 
// res21: Long = 850711696 

[OK]を選択すると、100行より約10倍大きくなります。テストからは、100行を超えて直線的に増加します。これらのテストに基づいて、約100行後、Rowオブジェクトあたりのコストはまだ800 KBを超えています !!

好奇心を持たずに、私は同じ根底にあるデータに対して2種類のオブジェクトタイプを試しました。例えば、ここでArrayArrayのオブジェクトの結果の代わりに、オブジェクトです:

SizeEstimator.estimate(
    df.map(r => (r.getString(0), r.getLong(1), r.getString(2), r.getDouble(3))).take(1) 
) 
// res22: Long = 216 

は、[OK]を、それは少しはましです。さらに良いのは、10行の場合は1976バイトのみであり、100の場合は19,616バイトであることです。確かに正しい方向に行く。

そして、Iは、基礎となるDataFrameと同じスキーマで、各Array[Byte]バイナリエンコードAvroレコードであるRDD[Array[Byte]]同じDataFrameをコードしていました。それから、私はこうします:

SizeEstimator.estimate(encodedRdd.take(1)) 
// res23: Long = 72 

72バイト - さらに良い!そして、100行の場合、それは5,216バイトであり、行は約52バイトであり、そこから降り続ける(1,000レコードの場合、48,656バイト)。それは最高の状態で同じデータのバイナリAvro記録は約50バイトであるのに対し

ので、オブジェクトは、あたり850Kの重さ。

何が起こっていますか?

+1

ではありません。これは推移的なオブジェクトグラフのバイト数を評価しています。これは、Rowオブジェクト自体がメモリやディスクにどれだけの記憶域を取っているかとはまったく関係ありません。パーティションブーキングなどで、おそらく巨大なキャッシュデータ構造を数えているでしょう。 –

+0

@SeanOwenここでは奇妙なことが起きています。それは 'StructField'のすべてであるようです。 [最小限の例で要点を作成しました](https://gist.github.com/zero323/0a8ef3636a6c4414d1bfcb9e6bfef94c)(コンテキストなし、 'DataFrames'、単一' StructField')、それでも不条理な数字が出ます(271872172) 。 – zero323

+1

私はこの方法であなたが期待している数字を与えることはないと思います。 Rowオブジェクトとデータフィールドだけで消費されるメモリの量をどうにかしようとするわけではありません。それは推移的オブジェクトグラフ全体のどれがどれほど大きなものかを示しています。そのほとんどは行データではありません。 –

答えて

5

実際にははあまり大きくありません。そのため、行数を増やすとサイズが大幅に変化することはありません。

class GenericRowWithSchema(values: Array[Any], 
    override val schema: StructType) 
  • が、これは本当にソースであることを確認しましょう:あなたは、データを収集するとき

    1. は、あなたが実際にschema引数からGenericRowWithSchema

      val df = Seq((1, "foo"), (2, "bar")).toDF 
      df.first.getClass 
      
      // res12: Class[_ <: org.apache.spark.sql.Row] = 
      // class org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema 
      
    2. GenericRowWithSchema carries schema informationを得る:問題は、スキーマ情報のようです問題の:

      import com.madhukaraphatak.sizeof.SizeEstimator 
      import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema 
      
      val rowWithSchema = df.first 
      val rowWithoutSchema = new GenericRowWithSchema(
          rowWithSchema.toSeq.toArray, null) 
      
      SizeEstimator.estimate(rowWithSchema) 
      // Long = 1444255708 
      
      SizeEstimator.estimate(rowWithoutSchema) 
      // Long = 120 
      
    3. 仮説:

      SizeEstimator.estimate(df.schema) 
      // Long = 1444361928 
      

      略収集行と同じオーダーである:あなたが見る推定サイズは、スキーマのサイズを含みます。

      import org.apache.spark.sql.types._ 
      
      val schema = StructType(Seq(
          StructField("_1",IntegerType,false), 
          StructField("_2",StringType,true))) 
      
      
      val anotherRowWithSchema = new GenericRowWithSchema(
          Array(0, "foo"), schema) 
      
      SizeEstimator.estimate(anotherRowWithSchema) 
      // Long = 1444905324 
      

      このように、結果が一貫していることがわかります。

    4. なぜスキーマが大きいのですか?言いにくい。コードを見ると、StructTypeは複雑なクラスであり、シンプルなスキーマ定義ではなく、コンパニオンオブジェクトも除外されています。

      報告されたサイズについては説明しませんが、私はそれがSizeEstimatorのいくつかの流行になる可能性があると思うが、私はまだ分かっていない。

    5. あなたは、さらに問題を切り分けが、単一StructFieldの大きさを推定することができます。これは、SizeEstimatorが何のためにあるのか

      import org.apache.spark.sql.types._ 
      import com.madhukaraphatak.sizeof.SizeEstimator 
      
      object App { 
          def main(args: Array[String]) { 
          val schema = StructField("foo", IntegerType, true) 
          println(SizeEstimator.estimate(schema)) 
          // 271872172 
          } 
      } 
      
  • +0

    しかし、なぜ線形が100行を超えて増加するのですか? –

    +0

    もちろん、聖なるもの81MBはスキーマのためにたくさんあります。 Avroのスキーマサイズは3964バイトです。Avroレコードをバイナリごとに含めても、800kではなく4kになります。 –

    +0

    @DavidGriffinまだ分かりません。私は 'StructField'自体や' SizeEstimator'に問題があるのか​​どうか分かりません。 – zero323

    関連する問題