这个是spark1.4后引入的一个东西,他的目的主要是提高内存和CPU的利用率。感觉挺奇怪的,不是一直说瓶颈在于IO和网络带宽么,怎么现在还来提高内存和CPU的利用率了?为啥就说CPU和内存就说瓶颈了?
在作者的博客上,他是这样说的
Why is CPU the new bottleneck? There are many reasons for this. One is that hardware configurations offer increasingly large aggregate IO bandwidth, such as 10Gbps links in networks and high bandwidth SSD’s or striped HDD arrays for storage. From a software perspective, Spark’s optimizer now allows many workloads to avoid significant disk IO by pruning input data that is not needed in a given job. In Spark’s shuffle subsystem, serialization and hashing (which are CPU bound) have been shown to be key bottlenecks, rather than raw network throughput of underlying hardware. All these trends mean that Spark today is often constrained by CPU efficiency and memory pressure rather than IO.
大意就是,一方面由于硬件的性能提升可以提供更大的带宽比如10G的网络,还有SSD或者HDD列阵,另一方面从软件上来说,spark也尽可能的做了优化,比如减少对job中不需要的数据的输入,以此来避免IO。而shuffle中需要序列化和哈希,这个都是CPU密集型计算,所以这块已经成为了瓶颈。
那么Tungsten主要做了哪些呢?
1.2Memory Management and Binary Processing: leveraging application semantics to manage memory explicitly and eliminate the overhead of JVM object model and garbage collection
2. Cache-aware computation: algorithms and data structures to exploit memory hierarchy
3. Code generation: using code generation to exploit modern compilers and CPUs
简而言之就是三个方面:1 内存管理上,2 cache上,3 代码优化上。
下面来简单介绍着三方面
##内存管理
我们知道,spark是基于jvm的,而之前也介绍过一些jvm的基本信息,jvm可以看做是一个虚拟机,所以他本身提供了内存管理,并且spark以前的版本也都是利用jvm的内存管理的,但是为毛现在就不想用它了呢?主要的问题就是jvm的内存管理做的不好,还有他的GC。这里举个栗子,java的对象往往会被包装上很多东西,例如一个"abcd"字符串,如果是C语言,那么就是4个字节,但是在java中却要48字节,因为java采用utf-16编码,并且每个字符串会有12字节的头和8字节的hash码。再加上对象的包装,一共会造成48字节的开销。另外的一个问题是,java的GC也会造成很大的开销。为毛呢?首先jvm的GC是一个通用的模型,他一般会将对象分为两类,一个young一个old,然后young会经常被GC,而old则不太会被GC。这种方法能有个好的效果的前提是,你能可靠的估计各个对象的生命周期。而spark作为一个特定的应用,他通过血缘关系可以很清楚的了解数据在各个stage,task的关系,所以spark相比jvm知道更多数据的信息,所以spark可以更好的了解数据的生命周期,所以spark应当自己来管理内存,而不是通过jvm。
但是问题来了,因为spark还是基于jvm来做的,就是说他还是通过jvm来执行机器码,那么我们又怎么能回避jvm来做内存管理呢?jvm的sun.misc.Unsafe提供了一个类C的内存接口,可以采用显示的分配,删除,指针等
##cache缓存
上过体系结构的都知道,为了降低内存和cpu之间的差距,通常会利用cache缓存,目前的电脑一般都会有好几级缓冲L1,L2,L3。能采用缓冲就是因为数据的局部性原理,即访问的数据都是在一块连续的区域,但是在java中的对象存储往往不是连续存储的,他的存储方式为通过栈的数据来引用堆中的数据。即栈中放引用,堆中放具体的实例。所以这种不连续的内存会大幅提高cache miss,这样就没法发挥缓存的作用了,导致cpu大部分的时间都是在等待内存数据加载到cache上。spark设计了一种cache友好的方法,可以提高cache命中率。例如对于排序算法,在shuffle中spark采用sore方式,所以排序比较重要。
传统的sort是通过指针来索引数据,但是缺点是cpu的cache命中率不高,需要随机访问recore做比较,而快排算法是可以很好的利用cache,但是record并不是连续存储的。新的方法则是将key和point结合起来,通过key排序,通过pointer进行查找,这样就能实现快速排序了。
##代码生成
这块自己的功底不够,没能理解