Apache Spark:核心数与执行者数量

时间:2014-07-08 00:46:52

标签: hadoop apache-spark yarn

我试图了解在YARN上运行Spark作业时内核数量和执行程序数量之间的关系。

测试环境如下:

  • 数据节点数:3
  • 数据节点机器规格:
    • CPU:Core i7-4790(核心数:4,线程数:8)
    • RAM:32GB(8GB x 4)
    • HDD:8TB(2TB x 4)
  • 网络:1Gb

  • Spark版本:1.0.0

  • Hadoop版本:2.4.0(Hortonworks HDP 2.1)

  • Spark工作流程:sc.textFile - >过滤器 - >地图 - >过滤器 - > mapToPair - > reduceByKey - >地图 - > saveAsTextFile

  • 输入数据

    • 类型:单个文本文件
    • 尺寸:165GB
    • 行数:454,568,833
  • 输出

    • 第二次过滤后的行数:310,640,717
    • 结果文件的行数:99,848,268
    • 结果文件的大小:41GB

该作业使用以下配置运行:

  1. --master yarn-client --executor-memory 19G --executor-cores 7 --num-executors 3(每个数据节点的执行者,使用核心)

  2. --master yarn-client --executor-memory 19G --executor-cores 4 --num-executors 3(核心数量减少)

  3. --master yarn-client --executor-memory 4G --executor-cores 2 --num-executors 12(少核心,更多执行者)

  4. 经过的时间:

    1. 50分15秒

    2. 55分钟48秒

    3. 31分23秒

    4. 令我惊讶的是,(3)更快 我认为(1)会更快,因为改组时执行者间的沟通会更少 虽然(1)的核心数少于(3),但核心数不是关键因素,因为2)表现良好。

      (在pwilmot回答之后添加了以下内容。)

      有关信息,性能监视器屏幕截图如下:

      • (1)的Ganglia数据节点摘要 - 作业于04:37开始。

      Ganglia data node summary for (1)

      • (3)的Ganglia数据节点摘要 - 作业于19:47开始。请在此之前忽略图表。

      Ganglia data node summary for (3)

      图表大致分为两部分:

      • 首先:从开始到reduceByKey:CPU密集型,无网络活动
      • 第二:在reduceByKey之后:CPU降低,网络I / O完成。

      如图所示,(1)可以使用尽可能多的CPU功率。所以,它可能不是线程数量的问题。

      如何解释这个结果?

8 个答案:

答案 0 :(得分:48)

  

为了让所有这些更加具体,这里有一个配置Spark应用程序以使用尽可能多的集群的工作示例   可能:想象一下,每个运行NodeManagers的六个节点的集群   配备 16核和64GB内存。 NodeManager的容量,   yarn.nodemanager.resource.memory-mb和   yarn.nodemanager.resource.cpu-vcores,应该设置为63 *   1024 = 64512(兆字节)和15。我们避免分配100%   YARN容器的资源,因为节点需要一些   运行操作系统和Hadoop守护进程的资源。在这种情况下,我们留下一个   千兆字节和这些系统进程的核心。 Cloudera Manager帮助   通过考虑这些并配置这些YARN属性   自动。

     

可能的第一个冲动是使用 - num-executors 6   --executor-cores 15 --execeror-memory 63G 。但是,这是错误的方法,因为:

     

63GB +执行程序内存开销不适合63GB容量   NodeManagers。应用程序主机将在一个核心上占用核心   节点,意味着15核心执行者没有空间   在那个节点上。每个执行程序15个核心可能导致错误的HDFS I / O.   吞吐量。

     

更好的选择是使用 - num-executors 17   --executor-cores 5 --executor-memory 19G 。为什么呢?

     

此配置会在除一个节点之外的所有节点上生成三个执行程序   与AM,将有两个执行者。   --executor-memory派生为(每个节点63/3执行程序)= 21. 21 * 0.07 = 1.47。 21 - 1.47~19。

解释是在cloudera博客的一篇文章中给出的 http://blog.cloudera.com/blog/2015/03/how-to-tune-your-apache-spark-jobs-part-2/

答案 1 :(得分:14)

根据Sandy Ryza

,在HDFS上运行你的火花应用程序
  

我注意到HDFS客户端遇到大量并发问题   线程。一个粗略的猜测是,每个执行者最多五个任务   实现完全写入吞吐量,所以保持数量是很好的   每个执行者的核心数低于该数字。

所以我认为你的第一个配置比第三个慢,因为HDFS I / O吞吐量不好

答案 2 :(得分:11)

我自己还没有使用过这些设置,所以这只是推测,但如果我们将此问题视为分布式系统中的普通内核和线程,那么在群集中最多可以使用12个内核(4 * 3)机器)和24个线程(8 * 3机器)。在前两个示例中,您为工作提供了相当数量的核心(可能的计算空间),但在这些核心上运行的线程(作业)数量非常有限,以至于您无法使用大部分处理即使分配了更多的计算资源,分配的功率也因此作业速度较慢。

你提到你的问题是在洗牌步骤中 - 尽管限制洗牌步骤的开销是很好的,但通常更重要的是利用集群的并行化。考虑一下极端情况 - 单线程程序,零洗牌。

答案 3 :(得分:2)

来自RStudio's Sparklyr package page优秀资源:

  

SPARK定义

     

提供一些简单的定义可能很有用   对于Spark命名法:

     

节点:服务器

     

工作线程:作为群集一部分且可用的服务器   运行Spark作业

     

主节点:协调工作节点的服务器。

     

执行程序:节点内的一种虚拟机。一个节点可以有   多个执行人员。

     

驱动程序节点:启动Spark会话的节点。通常情况下,   这将是sparklyr所在的服务器。

     

驱动程序(执行程序):驱动程序节点也将显示在执行程序中   列表。

答案 4 :(得分:2)

  

简短的答案:我认为tgbaggio是正确的。您对执行程序达到了HDFS吞吐量限制。

我认为这里的答案可能比这里的一些建议更简单。

对我来说,线索在群集网络图中。对于运行1,利用率稳定在〜50 M字节/秒。对于运行3,稳定利用率提高了一倍,约为100 M字节/秒。

the cloudera blog post共享的DzOrd中,您可以看到以下重要报价:

  

我注意到HDFS客户端在处理大量并发线程时遇到了麻烦。一个粗略的猜测是,每个执行者最多可以完成五个任务,以实现完整的写入吞吐量,因此最好将每个执行者的内核数量保持在该数量以下。

因此,让我们做一些计算,看看如果这是真的,我们会期望什么性能。


运行1:19 GB,7核,3个执行器

  • 3个执行程序x 7个线程= 21个线程
  • 每个执行者具有7个内核,我们希望对HDFS的IO有限(最多可容纳约5个内核)
  • 有效吞吐量〜= 3个执行程序x 5个线程= 15个线程

运行3:4 GB,2个内核,12个执行程序

  • 2个执行程序x 12个线程= 24个线程
  • 每个执行者2个内核,因此hdfs吞吐量还可以
  • 有效吞吐量〜= 12个执行程序x 2个线程= 24个线程

如果作业是100%受并发(线程数)限制的。我们希望运行时与线程数完全成反比。

ratio_num_threads = nthread_job1 / nthread_job3 = 15/24 = 0.625
inv_ratio_runtime = 1/(duration_job1 / duration_job3) = 1/(50/31) = 31/50 = 0.62

所以ratio_num_threads ~= inv_ratio_runtime,看来我们的网络有限。

相同的效果说明了运行1和运行2之间的区别。


运行2:19 GB,4核,3个执行程序

  • 3个执行程序x 4个线程= 12个线程
  • 每个执行器具有4个内核,可以对HDFS进行IO
  • 有效吞吐量〜= 3个执行程序x 4个线程= 12个线程

比较有效线程数和运行时:

ratio_num_threads = nthread_job2 / nthread_job1 = 12/15 = 0.8
inv_ratio_runtime = 1/(duration_job2 / duration_job1) = 1/(55/50) = 50/55 = 0.91

它不如上次比较完美,但是当丢失线程时,我们仍然会看到类似的性能下降。

现在要讲的最后一点:为什么在更多线程下我们可以获得更好的性能,特别是。比CPU数量更多的线程?

Rob在这篇很棒的文章中很好地解释了并行性(我们将数据划分到多个CPU上获得的东西)和并发性(当我们使用多个线程在单个CPU上工作时得到的东西)之间的区别。派克:Concurrency is not parallelism

简短的解释是,如果Spark作业正在与文件系统或网络进行交互,则CPU将花费大量时间等待与这些接口的通信,而实际上并没有花费大量时间来“工作”。通过一次为这些CPU提供多个任务来工作,他们将花费更少的等待时间和更多的时间来工作,并且您会看到更好的性能。

答案 5 :(得分:0)

我认为其中一个主要原因是地方性。您的输入文件大小为165G,文件的相关块肯定分布在多个DataNode上,更多的执行程序可以避免网络拷贝。

尝试设置executor num equal blocks count,我认为可以更快。

答案 6 :(得分:0)

Spark动态分配提供了灵活性并动态分配资源。在这个数量的最小和最大执行者可以给出。此外,还可以给出在应用程序启动时必须启动的执行程序的数量。

请阅读以下内容:

  

http://spark.apache.org/docs/latest/configuration.html#dynamic-allocation

答案 7 :(得分:0)

我认为前两种配置存在一个小问题。线程和核心的概念如下。线程的概念是,如果核心是理想的,那么使用该核心来处理数据。因此在前两种情况下没有充分利用内存。如果您想在此示例中进行基准测试,请选择每台计算机上具有 10核的计算机。然后做基准测试。

但是,如果每个执行程序不超过5个核心,那么i / o性能将会出现瓶颈。

因此,执行此基准测试的最佳机器可能是具有10个核心的数据节点。

数据节点机规格: CPU:Core i7-4790(核心数:10,线程数:20) 内存:32GB(8GB x 4) 硬盘:8TB(2TB x 4)