2015-09-04 9 views
6

、私は、次の構造を有する:PairRDDScalaコンパイラは未使用の変数値をどのように処理しますか?スカラとスパークを使用

val rdd1: RDD[String] = ... 
val rdd2: RDD[(String, Any)] = ... 

val rdd1pairs = rdd1.map(s => (s, s)) 
val result = rdd2.join(rdd1pairs) 
       .map { case (_: String, (e: Any, _)) => e } 

マッピングrdd1の目的は、後の工程でrdd2との結合です。しかし、私は実際にはrdd2という値だけに興味があります。したがって、キーを省略した最後の行のマッピングステップです。実際には、これは、効率の理由から、rdd2rdd1の間の交差点であり、Sparkのjoin()で実行されます。

私の質問はrdd1pairsのキーを参照しています。最初のマップステップで構文上の理由で(結合を許可するために)作成され、後で使用せずに破棄されます。コンパイラはこれをどのように処理しますか?例のように、文字列sを使用するかどうかはメモリ消費の点で重要ですか?少しメモリを節約するには、nullまたは0に置き換える必要がありますか?コンパイラは実際にこれらのオブジェクト(参照)を作成して保存しますか、使用されないことに気づきますか?

答えて

3

この場合、コンパイラではなく、結果に影響を与えるのはSparkドライバです。 sの重複した複製を作成しないように、Sparkが実行パイプラインを最適化できるかどうか。私は確信していませんが、私はスパークがメモリにrdd1pairsを作成すると思います。 (String, String)

代わりのマッピングあなたは(String, Unit)使用できます。何をやっている

rdd1.map(s => (s,())) 

を基本的にrdd1に基づいてrdd2のフィルタです。 rdd1がrdd2よりもかなり小さい場合、別の方法は、rdd1のデータをRDDではなくブロードキャスト変数として表現し、単純にrdd2をフィルタリングすることです。これにより、シャッフルまたはフェーズの削減が回避されるため、より速くなる可能性がありますが、rdd1のデータが各ノードに収まるのに十分小さい場合にのみ機能します。

EDIT:この質問で説明したようにjstatコマンドを使用して

object size extends App { 

    (1 to 1000000).map(i => ("foo"+i,())) 
    val input = readLine("prompt> ") 
} 

object size extends App { 

    (1 to 1000000).map(i => ("foo"+i, "foo"+i)) 
    val input = readLine("prompt> ") 
} 

How to check heap usage of a running JVM from the command line?

がユニットを使用してどのように考えるのではなく文字列は、スペースを節約し、次の例を考えます最初のバージョンは後者よりもヒープの使用量が大幅に少なくなります。

編集2:

Unitは、それがどのシリアル化を要求すべきではないので、論理的、効果的に無内容のシングルトンオブジェクトです。型定義にUnitが含まれているということは、Unit型のフィールドを持つ構造体を逆シリアル化できる必要があることをすべて示しています。

SparkはデフォルトでJavaシリアル化を使用します。次のことを考えてみましょう:

object Main extends App { 

    import java.io.{ObjectOutputStream, FileOutputStream} 

    case class Foo (a: String, b:String) 
    case class Bar (a: String, b:String, c: Unit) 

    val str = "abcdef" 
    val foo = Foo("abcdef", "xyz") 
    val bar = Bar("abcdef", "xyz",()) 

    val fos = new FileOutputStream("foo.obj") 
    val fo = new ObjectOutputStream(fos) 
    val bos = new FileOutputStream("bar.obj") 
    val bo = new ObjectOutputStream(bos) 
    fo writeObject foo 
    bo writeObject bar 
} 

2つのファイルは同じサイズのものである:

�� sr Main$Foo3�,�z \ L at Ljava/lang/String;L bq ~ xpt abcdeft xyz 

�� sr Main$Bar+a!N��b L at Ljava/lang/String;L bq ~ xpt abcdeft xyz 
+0

は合理的ですね、ありがとう。しかし、私は、Unitへの参照をどのようにして元の文字列バリアントと比較してかなりの量のメモリを保存するのかまだまだわかりません。それは? – Carsten

+0

そのトピックをカバーするために私の答えを拡張しました – mattinbits

+1

しかし、元の質問では新しい文字列は作成されません。文字列への参照は、 '()'への参照と同じサイズです。 –

関連する問題