云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

分布式存储_哪的云主机便宜_价格

小七 141 0

钨计划:让Apache Spark更接近裸金属

在之前的一篇博客文章中,我们回顾并调查了过去一年中apachespark的性能改进。在这篇文章中,我们期待并与您分享下一章,我们称之为钨计划。2014年,Spark创造了大规模排序的世界纪录,从Python到SQL再到机器学习,整个引擎都有了重大改进。然而,性能优化是一个永无止境的过程。钨丝计划将是Spark自项目开始以来执行引擎的最大变化。它致力于大幅提高Spark应用程序的内存和CPU效率,将性能推向现代硬件的极限。这项工作包括三项举措:内存管理和二进制处理:利用应用程序语义显式地管理内存,消除JVM对象模型和垃圾收集的开销缓存感知计算:利用内存层次结构的算法和数据结构代码生成:使用代码生成来开发现代编译器和CPU对CPU效率的关注是因为Spark工作负载越来越受到CPU和内存使用的限制,而不是IO和网络通信。最近对大数据工作负载性能的研究(Ousterhout等人)表明了这一趋势,我们也得出了类似的结论,这是我们为Databricks云客户持续调整和优化工作的一部分。为什么CPU是新的瓶颈?原因有很多。一是硬件配置提供了越来越大的聚合IO带宽,如网络中的10Gbps链路和用于存储的高带宽SSD或条带化HDD阵列。从软件的角度来看,Spark的优化器现在允许许多工作负载通过修剪给定作业中不需要的输入数据来避免显著的磁盘IO。在Spark的shuffle子系统中,序列化和散列(这是CPU限制的)已经被证明是关键的瓶颈,而不是底层硬件的原始网络吞吐量。所有这些趋势都意味着Spark现在常常受到CPU效率和内存压力的限制,而不是IO。1内存管理和二进制处理JVM上的应用程序通常依赖JVM的垃圾收集器来管理内存。JVM是一项令人印象深刻的工程壮举,它被设计成用于许多工作负载的通用运行时。然而,随着Spark应用程序突破性能边界,JVM对象和GC的开销变得不可忽略。Java对象有很大的固有内存开销。考虑一个简单的字符串"abcd",使用UTF-8编码存储它需要4个字节。然而,JVM的本机字符串实现以不同的方式存储这些信息,以方便更常见的工作负载。它使用UTF-16编码,使用2个字节对每个字符进行编码,每个字符串对象还包含一个12字节的头和8个字节的哈希代码,如下Java对象布局工具的输出所示。java.lang.String对象内部:偏移尺寸类型说明值0 4(对象标头)。。。4 4(对象标题)。。。8 4(对象标题)。。。12 4字符[]字符串.value                  []16 4内景字符串.哈希020 4内景字符串.hash320实例大小:24字节(由检测API报告)在JVM对象模型中,一个简单的4字节字符串总共超过48个字节!JVM对象模型的另一个问题是垃圾收集的开销。在较高的层次上,分代垃圾收集将对象分为两类:一类是分配/释放率较高的一类(年轻一代),另一类是保留在周围的(老一代)。垃圾收集器利用年轻一代对象的瞬态特性来高效地管理它们。当GC可以可靠地估计对象的生命周期时,这种方法很有效,但是如果不进行估计(即一些暂时的对象溢出到旧的一代中),那么这种方法就不够了。由于JVM的生命周期最终需要数十个参数的试探性调整,因此需要更多的关于JVM生命周期的信息。然而,Spark并不仅仅是一个通用的应用程序。Spark了解数据如何在计算的各个阶段流动,以及工作和任务的范围。因此,Spark比JVM垃圾收集器知道更多关于内存块生命周期的信息,因此应该能够比JVM更有效地管理内存。为了解决对象开销和GC的低效率,我们引入了一个显式内存管理器,将大多数Spark操作转换为直接针对二进制数据而不是Java对象进行操作。这是建立在sun.misc.不安全,JVM提供的一种高级功能,它公开C风格的内存访问(例如显式分配、释放、指针算法)。此外,不安全的方法是内在的,这意味着每个方法调用都由JIT编译成一条机器指令。已在某些内存区域显式地使用Spark管理。去年,Databricks提供了一种新的基于Netty的网络传输,它使用类似jemalloc的内存管理器显式地管理所有网络缓冲区。这对于扩大Spark的shuffle操作和赢得Sort基准测试至关重要。第一部分将出现在Spark 1.4中,其中包括一个哈希表,该表直接对二进制数据进行操作,内存由Spark显式管理。与标准的javahashmap相比,这个新实现的间接开销要小得多,并且垃圾收集器看不到它。这项工作仍在进行中,但初步业绩令人鼓舞。如上所示,我们比较使用不同哈希映射的聚合操作的吞吐量:一个使用新的哈希映射的堆模式,一个使用offheap,另一个使用java.util.HashMap. 新的哈希表在单个线程中每秒支持超过100万个聚合操作,大约是java.util.HashMap. 更重要的是,在不调整任何参数的情况下,它几乎不会随着内存利用率的增加而降低性能,而JVM默认值最终会因GC而崩溃。在Spark 1.4中,这个散列映射将用于数据帧和SQL的聚合,而在1.5中,我们将为大多数其他操作准备好数据结构,例如排序和联接。在许多情况下,这将消除对GC进行调优以获得高性能的需要。2缓存感知计算在解释缓存感知计算之前,让我们回顾一下"内存中"计算。Spark被广泛认为是内存计算引擎。这个术语的真正含义是Spark可以有效地利用集群上的内存资源,处理数据的速度远远高于基于磁盘的解决方案。然而,Spark还可以处理大于可用内存数量级的数据,透明地溢出到磁盘并执行诸如排序和散列之类的外部操作。类似地,缓存感知计算通过更有效地使用L1/L2/L3 CPU缓存来提高数据处理的速度,因为它们比主内存快几个数量级。在分析Spark用户应用程序时,我们发现大部分CPU时间都花在等待从主内存中获取数据。作为钨计划的一部分,我们正在设计缓存友好的算法和数据结构,这样Spark应用程序将花费更少的时间等待从内存中获取数据,而将更多的时间用于做有用的工作。以记录排序为例。标准的排序过程将存储指向记录的指针数组,并使用快速排序来交换指针,直到所有记录都被排序。由于顺序扫描访问模式,排序通常具有良好的缓存命中率。但是,对指针列表排序的缓存命中率很低,因为每次比较操作都需要取消引用指向内存中随机定位的记录的两个指针。那么我们如何改进排序的缓存局部性呢?用一个非常简单的方法来存储每一个键的指针。例如,如果排序键是64位整数,那么我们使用128位(64位指针和64位密钥)将每个记录存储在指针数组中。这样,每个快速排序比较操作都只以线性方式查找指针-密钥对,不需要随机内存查找。希望上面的插图能让您了解如何重新设计基本操作以实现更高的缓存位置。这如何适用于Spark?大多数分布式数据处理可以归结为一个小的操作列表,例如聚合、排序和联接。通过提高这些操作的效率,我们可以从整体上提高Spark应用程序的效率。我们已经构建了一个支持缓存的sort版本,比前一个版本快3倍。这种新的排序将用于基于排序的洗牌、高基数聚合和排序合并联接运算符。到今年年底,大多数Spark的最低级别算法将升级为缓存感知,从而提高从机器学习到SQL的所有应用程序的效率。三。代码生成大约一年前,Spark引入了SQL和数据帧中表达式计算的代码生成。表达式求值是计算特定记录上表达式值的过程(例如"age>35&&age