web-dev-qa-db-ja.com

sparkで各エグゼキュータで1つの操作を1回実行する方法

S3に格納された約400MBのサイズのwekaモデルがあります。これで、モデルを実行して予測を実行するレコードのセットができました。

予測を実行するために、私が試したのは、

  1. モデルを静的オブジェクトとしてドライバーにダウンロードしてロードし、すべてのエグゼキューターにブロードキャストします。予測RDDでマップ操作を実行します。 ---->予測を実行するためのWekaのように、モデルオブジェクトを変更する必要があり、ブロードキャストには読み取り専用コピーが必要なため、機能しません。

  2. モデルを静的オブジェクトとしてドライバーにダウンロードしてロードし、各マップ操作でエグゼキューターに送信します。 ----->動作(各マップ操作のように効率的ではありません。400MBのオブジェクトを渡します)

  3. ドライバーでモデルをダウンロードし、各エグゼキューターにロードしてそこにキャッシュします。 (それを行う方法がわからない)

誰かが各エクゼキューターにモデルを一度ロードして、他のレコードのためにモデルを再度ロードしないようにキャッシュする方法を知っていますか?

25
Neha

次の2つのオプションがあります。

1.データを表す遅延valを持つシングルトンオブジェクトを作成します。

    object WekaModel {
        lazy val data = {
            // initialize data here. This will only happen once per JVM process
        }
    }       

次に、map関数で遅延valを使用できます。 lazy valは、各ワーカーJVMがデータの独自のインスタンスを初期化するようにします。 dataのシリアル化またはブロードキャストは実行されません。

    elementsRDD.map { element =>
        // use WekaModel.data here
    }

利点

  • jVMインスタンスごとにデータを初期化できるため、より効率的です。このアプローチは、たとえばデータベース接続プールを初期化する必要がある場合に適しています。

短所

  • 初期化の制御が少ない。たとえば、実行時パラメーターが必要な場合は、オブジェクトを初期化するのが難しくなります。
  • 必要な場合、オブジェクトを実際に解放または解放することはできません。通常、プロセスの終了時にOSがリソースを解放するため、これは受け入れられます。

2. RDDでmapPartitionだけでなくforeachPartition(またはmap)メソッドを使用します。

これにより、パーティション全体に必要なものをすべて初期化できます。

    elementsRDD.mapPartition { elements =>
        val model = new WekaModel()

        elements.map { element =>
            // use model and element. there is a single instance of model per partition.
        }
    }

利点

  • オブジェクトの初期化および初期化解除の柔軟性を高めます。

短所

  • 各パーティションは、オブジェクトの新しいインスタンスを作成して初期化します。 JVMインスタンスごとにいくつのパーティションがあるかによって、問題になる場合とそうでない場合があります。
26
Dia Kharrat

遅延イニシャライザよりも優れているのは次のとおりです。 nullに初期化されたオブジェクトレベルのポインターを作成し、各エグゼキューターに初期化させます。初期化ブロックでは、1回限りのコードを使用できます。各処理バッチはローカル変数をリセットしますが、オブジェクトレベルの変数はリセットしないことに注意してください。

object Thing1 {
  var bigObject : BigObject = null

  def main(args: Array[String]) : Unit = {
    val sc = <spark/scala magic here>
    sc.textFile(infile).map(line => {
      if (bigObject == null) {
         // this takes a minute but runs just once
         bigObject = new BigObject(parameters)  
      }
      bigObject.transform(line)
    })
  }
}

このアプローチでは、他のアプローチのパーティションごとに1つの大きなオブジェクトではなく、エグゼキューターごとに1つの大きなオブジェクトが作成されます。

メイン関数名前空間内にvar bigObject:BigObject = nullを配置すると、動作が異なります。その場合、各パーティション(つまり、バッチ)の先頭でbigObjectコンストラクターを実行します。メモリリークがある場合、最終的にはエグゼキュータが強制終了されます。ガベージコレクションには、さらに作業が必要です。

1
Dale Johnson