Spark Cache性能测试

采用Spark自带的Kmeans算法作为测试基准(Spark版本为2.1),该算法Shuffle数据量较小,对于这类迭代型任务,又需要多次加载训练数据,此测试的目的在于评判各种Cache IO的性能,并总结其Spark内部原理作分析,作为Spark用户的参考。

测试准备

训练数据是通过Facebook SNS公开数据集生成器得到,在HDFS上大小为9.3G,100个文件,添加如下两个参数以保证所有资源全部到位后才启动task,训练时间为加载数据到训练完毕这期间的耗时。

1
2
--conf spark.scheduler.minRegisteredResourcesRatio=1
--conf spark.scheduler.maxRegisteredResourcesWaitingTime=100000000

测试集群为3个节点的TS5机器搭建而成,其中一台作为RM,并运行着Alluxio Master,两个NM上同时运行着Alluxio Worker。除以上配置外,其他配置全部保持Spark默认状态。公共资源配置、分区设置以及算法参数如下表所示,executor_memory视不同的测试用例不同:

driver_memory num_executor executor_cores 分区数 聚类个数 迭代次数
4g 10 2 100 6 10

测试用例

测试用例1: 不使用Cache

在不使用Cache的情况下,测试Spark-Kmeans算法的训练时间以及GC时间占比。这里分别使用两种方式加载数据,一种是直接从HDFS加载数据,另一种是透过ALLUXIO加载数据,相关测试指标数据如下表所示:

\ 说明 内存使用总量 训练时间 GC时间占比
case 1-1 从hdfs直接加载训练数据 20g 1064s 3.1%
case 1-2 透过alluxio加载训练数据 20g + 9.3g 689s 3.4%

不使用cache时,以上两种情形GC均不是瓶颈,主要差别表现在:

  • 从hdfs直接加载训练数据:在每次迭代时均要读一遍hdfs,访问hdfs有较大的开销;
  • 透过alluxio加载训练数据:只需第一次加载读一遍hdfs,后续迭代直接从alluxio中读取,不过alluxio额外消耗9.3G内存,整体性能提升35%+。

测试用例2: 使用Cache

在使用Cache的情况下,从HDFS加载数据后先做cache,分别采用不同的Cache方式,相关测试指标数据如下表所示:

\ 缓存方式 executor_memory 内存使用总量 cache比例 训练时间 GC时间占比
case 2-1 MEMORY_ONLY 2g 20g 33% 1558s 12%
case 2-2 MEMORY_ONLY 4g 40g 90% 986s 7%
case 2-3 MEMORY_ONLY 6g 60g 100% 463s 4.7%
case 2-4 MEMORY_AND_DISK 2g 20g 100% 1182s 16.9%
case 2-5 DISK_ONLY 2g 20g 100% 514s 3.2%
case 2-6 ALLUXIO 2g 20g + 9.3g 100% 687s 4.5%

采用Alluxio的Cache实现方式为:

1
2
data.saveAsTextFile(path)
val data = sc.textFile(path)

从以上测试数据看来,让人有点出乎意料,一开始有点不太相信,但是多次测试后数据并没有多大的抖动,所以说Spark的性能受多方面因素的影响,单单Cache这块不同的Cache方式以及不同的资源情况下,其性能差别就相差较大,下面分析其内在原因。

从HDFS加载训练数据后直接采用Spark原生的Cache,当executor_memory为2g时,不足以Cache住原始训练数据,从UI上看到Cache的比例只有33%左右,导致频繁的 rdd-block 剔除重建,同时由于内存吃紧,可能引发频发的Spill以及较重的GC,从UI上看到GC时间占到总的task运行时间的12%左右,已经成为瓶颈,其整体性能还不如不使用Cache的case1-1;当executor_memory为4g时,也不足以Cache住原始训练数据,但是其Cache的比例有90%左右,同样存在 rdd-block 剔除重建,频发Spill以及较重的GC,GC时间占总的task运行时间的7%左右,虽然比executor_memory为2g的情况有所好转,但是仍然不理想,只比不做Cache的case1-1好7%左右,但是内存却多用了20g,并不是特别划算;当executor_memory为6g时,可以全部Cache住原始训练数据,性能较优,GC占比较小,但是比不用Cache的case1-1要多用40g内存,有些代价。

一般来说,当我们内存不够时,可以选择MEMORY_AND_DISK的缓存方式,但是测试发现MEMORY_AND_DISK的缓存效果并不是特别好,从测试数据来看,还不如直接使用DISK_ONLY的缓存方式,MEMORY_AND_DISK的缓存方式带来的GC开销非常大,可能是因为每次都尽可能地Cache数据到内存,不够再刷到磁盘,造成JVM频繁GC。

另外测试了使用Alluxio作缓存的Case,发现并没有官方描述的那样会提升Cache的性能,还不如直接使用Spark DISK_ONLY缓存,感觉官方给的测试对比数据存在一定的水分,值得一提的是在多个Application之间Alluxio能起到加速作用。

小结

Spark的Cache并不是总是会加速任务运行,Cache的方式不同,对任务产生的影响不同。并不是能用内存Cache就用内存,而是要考虑是否有充足的内存Cache住你的数据,否则可能适得其反。

转载请注明出处,本文永久链接:https://sharkdtu.github.io/posts/spark-cache-benchmark.html