私はsparkアプリケーションをヤーンクラスターで実行します。私のコードでは、データセットにパーティションを作成するためにキューの利用可能なコアの数を使用しています。
Dataset ds = ...
ds.coalesce(config.getNumberOfCores());
私の質問:構成ではなくプログラムでキューの利用可能なコア数を取得するにはどうすればよいですか?
クラスター内のエグゼキューターの数とコアの数の両方をSparkから取得する方法があります。 Scala過去に使用したユーティリティコードの一部です。Javaに簡単に適応できるはずです。2つの重要なアイデアがあります。
ワーカーの数は、エグゼキューターの数から1を引いた数またはsc.getExecutorStorageStatus.length - 1
です。
ワーカーごとのコアの数は、ワーカーでJava.lang.Runtime.getRuntime.availableProcessors
を実行することで取得できます。
コードの残りの部分は、Scala implicitsを使用してSparkContext
に便利なメソッドを追加するためのボイラープレートです。1.x年前にコードを書いたため、SparkSession
を使用していません。
最後のポイントの1つは、データが偏っている場合にパフォーマンスを向上させることができるため、複数のコアに合体させることをお勧めすることがよくあります。実際には、データのサイズと、ジョブが共有クラスターで実行されているかどうかに応じて、1.5倍から4倍の範囲で使用します。
import org.Apache.spark.SparkContext
import scala.language.implicitConversions
class RichSparkContext(val sc: SparkContext) {
def executorCount: Int =
sc.getExecutorStorageStatus.length - 1 // one is the driver
def coresPerExecutor: Int =
RichSparkContext.coresPerExecutor(sc)
def coreCount: Int =
executorCount * coresPerExecutor
def coreCount(coresPerExecutor: Int): Int =
executorCount * coresPerExecutor
}
object RichSparkContext {
trait Enrichment {
implicit def enrichMetadata(sc: SparkContext): RichSparkContext =
new RichSparkContext(sc)
}
object implicits extends Enrichment
private var _coresPerExecutor: Int = 0
def coresPerExecutor(sc: SparkContext): Int =
synchronized {
if (_coresPerExecutor == 0)
sc.range(0, 1).map(_ => Java.lang.Runtime.getRuntime.availableProcessors).collect.head
else _coresPerExecutor
}
}
更新
最近、getExecutorStorageStatus
が削除されました。 SparkEnv
のblockManager.master.getStorageStatus.length - 1
を使用するように切り替えました(マイナス1はドライバー用です)。 env
のSparkContext
を介してこれにアクセスする通常の方法は、org.Apache.spark
パッケージの外部からはアクセスできません。したがって、カプセル化違反パターンを使用します。
package org.Apache.spark
object EncapsulationViolator {
def sparkEnv(sc: SparkContext): SparkEnv = sc.env
}
ほぼ同じ質問の答えを探しているときにこれを見つけました。
見つけた:
Dataset ds = ...
ds.coalesce(sc.defaultParallelism());
oPが求めていたものを正確に実行します。
たとえば、5ノードx 8コアクラスターは、defaultParallelism
に対して40を返します。
Databricks によると、ドライバとエグゼキュータが同じノードタイプである場合、これが方法です。
Java.lang.Runtime.getRuntime.availableProcessors * (sc.statusTracker.getExecutorInfos.length -1)
すべてのマシンでジョブを実行してコアの数を尋ねることもできますが、Sparkで利用できるものとは限りません(@tribbloidが別の回答のコメントで指摘しているように):
import spark.implicits._
import scala.collection.JavaConverters._
import sys.process._
val procs = (1 to 1000).toDF.map(_ => "hostname".!!.trim -> Java.lang.Runtime.getRuntime.availableProcessors).collectAsList().asScala.toMap
val nCpus = procs.values.sum
シェルで(2つのワーカーを持つ小さなテストクラスターで)実行すると、次のようになります。
scala> :paste
// Entering paste mode (ctrl-D to finish)
import spark.implicits._
import scala.collection.JavaConverters._
import sys.process._
val procs = (1 to 1000).toDF.map(_ => "hostname".!!.trim -> Java.lang.Runtime.getRuntime.availableProcessors).collectAsList().asScala.toMap
val nCpus = procs.values.sum
// Exiting paste mode, now interpreting.
import spark.implicits._
import scala.collection.JavaConverters._
import sys.process._
procs: scala.collection.immutable.Map[String,Int] = Map(ip-172-31-76-201.ec2.internal -> 2, ip-172-31-74-242.ec2.internal -> 2)
nCpus: Int = 4
通常、クラスターにlots台のマシンがある場合は、範囲にゼロを追加します。 2台のマシンのクラスターでも、10000は数秒で完了します。
これはおそらく、(@ SteveCの回答のように)sc.defaultParallelism()が提供するよりも多くの情報が必要な場合にのみ役立ちます。