Spark:以编程方式获取集群核心数

时间:2017-11-20 18:50:16

标签: java apache-spark dataset yarn core

我在纱线集群中运行我的火花应用程序。在我的代码中,我使用数量可用的队列核心来创建我的数据集上的分区:

Dataset ds = ...
ds.coalesce(config.getNumberOfCores());

我的问题:如何通过编程方式而不是按配置获取队列的可用核心数?

4 个答案:

答案 0 :(得分:11)

有一些方法可以从Spark中获取集群中的执行程序数和核心数。这是我过去使用过的一些Scala实用程序代码。您应该能够轻松地将其适应Java。有两个关键的想法:

  1. 工人人数是执行人数减1或sc.getExecutorStorageStatus.length - 1

  2. 可以通过对工作人员执行java.lang.Runtime.getRuntime.availableProcessors来获得每个工作人员的核心数。

  3. 代码的其余部分是样板,用于使用Scala implicits向SparkContext添加便捷方法。我在1.x年前编写了代码,这就是为什么它没有使用SparkSession

    最后一点:合并到多个核心通常是一个好主意,因为这可以在数据偏斜的情况下提高性能。实际上,我使用1.5x到4x之间的任何位置,具体取决于数据的大小以及作业是否在共享集群上运行。

    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
        }
    
    }
    

答案 1 :(得分:1)

在寻找几乎相同问题的答案时发现了这一点。

我发现:

Dataset ds = ...
ds.coalesce(sc.defaultParallelism());

完全符合OP的要求。

例如,我的5节点x 8核心群集返回defaultParallelism的40。

答案 2 :(得分:0)

根据Databricks,如果驱动程序和执行程序的节点类型相同,则可以采用以下方法:

java.lang.Runtime.getRuntime.availableProcessors * (sc.statusTracker.getExecutorInfos.length -1)

答案 3 :(得分:0)

您可以在每台机器上运行作业,并要求其提供内核数量,但这不一定适用于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

在外壳中运行它(在具有两个工作程序的小型测试集群上)会给出:

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
如果集群中通常有很多台计算机,请

在您的范围内添加零。甚至在我的两机集群上,10000也会在几秒钟内完成。

这可能仅在您需要更多信息而不是sc.defaultParallelism()会给您提供信息时才有用(如@SteveC的答案一样)