目录openGauss数据库SQL引擎openGauss数据库执行器技术一.openGauss数据库执行器概述
二.openGauss执行引擎
三.高级特性介绍
Ⅰ.编译执行
Ⅱ.向量化引擎
openGauss存储技术openGauss事务机制
openGauss数据库安全openGauss数据库执行器技术三.高级特性介绍
这个章节我们介绍下openGauss执行器里面几个高级特性,在介绍特性之前,先简单介绍下当前CPU体系架构里影响性能的几个关键因素,这些关键因素和其应对的技术构成了执行器里两个关键技术,编译执行和向量化引擎。
§函数调用:函数调用过程中需要维护参数和返回地址在栈帧的管理,处理完成之后还要调回到之前的栈帧,因此在用户的函数调用过程中,CPU要消耗额外的指令来进行函数调用上下文的维护。
§分支预测:指令在现代CPU中以流水线运行,当处理器遇到分支条件跳转时,通常不能确定执行那个分支,因此处理器采用分支预测来预测每条跳转指令是否会执行。如果猜测准确,那么流水线中就会充满指令,如果对跳转猜测错误,那么就要要求处理器丢掉它这个跳转指令后的所有已做的操作,然后再开始用从正确位置处起始的指令去填充流水线,可以看到这种预测错误会导致很严重的性能惩罚,会导致大约20-40个时钟周期的浪费,从而导致性能的严重下降。这里可以看到提速方式一共有两种。是更准确的智能预测,但是无论多么准确,总会存在误判,另外一种就是从根本上消除分支。
§CPU存取数据:CPU对于数据的存取存在鲜明的层次关系,在寄存器、CPU高速缓存(CACHE)、内存的存取速度越来越慢,所承载的容量越来越大。同时CPU在访问数据的时候也会遵循从快到慢的原则,比如CACHE中找不到的数据才会从内存中找,而这两者的访问速度差距在两个数量级。如果CPU的访问模式是线性的(比如访问数组),CPU会主动将后续的内存地址预加载到CACHE,这就是CPU的数据预取。因此程序如果能够充分利用到这个特征,将大大提速程序的性能。
§SIMD:单指令多数据流,对于计算密集型程序来说,可能经常会需要对大量不同的数据进行同样的运算。SIMD引入之前,执行流程为同样的指令重复执行,每次取一条数据进行运算。而SIMD可以一条指令执行多个位宽数据的计算。比如当前最新的体系结构已经支持位宽的SIMD指令,那么对于16位整型的加法,可以并行执行32个整型对的加法。
Ⅰ.编译执行在文章的表达式计算小节中,介绍了基于遍历树的表达式计算框架,这种框架的好处是清晰明了,但是在性能上却不是最优,主要有以下几个原因:
§表达式计算其框架的通用性决定了其执行模式要适配各种不同的操作符和数据类型,因此在运行时要根据其表达式遍历的具体结果来确定其执行的函数和类型,对这些类型的判断要引入非常多的分支判断。
§表达式计算在整体的执行过程中要进行多次的函数调用,其调用的深度取决于其树的深度,这一部分也有着非常大的开销。
这两个核心原因,分支判断和函数调用同样在执行算子中也是影响性能的关键因素,为了提升其执行速度,openGauss引入了业界著名的开源编译框架LLVM(LowLevelVirtualMachine)来提速的执行速度,LLVM是一个通用的编译框架,能够支持不同的计算平台。
LLVM提升整体表达式计算的核心要点如下:
1)openGauss内置的LLVM编译框架通过为每一个计算单元(表达式或者执行算子里面的热点函数)生成一段独特的执行代码,由于在编译的时候提前知道了表达式涉及的操作和数据类型,为这个表达式生成的执行代码将所有的逻辑内联,完全去除函数调用。
比如对于文章中提到的表达式计算过程,openGauss内置的LLVM编译为这个表达式生成了下面这样一段特殊代码。这里面已经没有任何其他的函数调用,所有的函数都已经被内联在一起,同时去掉了关于数据类型的分支判断。
Boolqual(){boolqual1res=2*w_tax+0.91;boolqual2res=w_city!=’Beijing’;Returnqual1resqual2res;}2)LLVM编译框架利用编译技术最大程度的让生成的代码将中间结果的数据存储在CPU寄存器里,让数据读取的速度加快。
Ⅱ.向量化引擎在的概要介绍中提到了执行器的数据流动模式:控制流向下、数据流向上。传统的执行引擎数据流遵循一次一元组的传输模式,而向量化引擎将这个模型改成一次一批元组的模式,这种看似简单的修改却带来巨大的性能提升,如图6所示。
图6单个元组与向量化元组对比其中的主要提升原因可以应对上面介绍的CPU架构里影响性能的几个关键因素。
§一次一元组的函数模型在控制流的调动下,每次都需要进行函数调用,调用次数随着数据增长而增长,而一批元组的模式则大大降低了执行节点的函数调用开销,如果我们设定一次一批的数量为,函数调用相对于一次一元组能减少三个数量级。
§一次一批元组的模式在内部实现通过数组来表达,数组对于CPU的预取非常友好,能够让数组在后续的数据处理过程中,大概率能够在CACHE中命中。
比如对于下面这个简单计算两个整形加法的表达式函数(其代码仅为了展示,不代表真实实现),下面展示了一次一元组和一次一批元组的两种写法:
一次一元组的整形加法:
intint4addint4(int4a,intb){Returna+b;}一次一批元组的整形加法:
voidint4addint4(int4a[],intb[],intres[]){for(inti=0;iN;i++)res[i]=a[i]+b[i];}一次一批元组的这个计算函数,因为CPUCACHE的局部性原理,数据和指令的cache命中率会非常好,极大提升处理性能。
§一次一批元组的数据数组化的组织方式为利用SIMD特性带来了非常好的机会,SIMD能够大大提升在元组上的计算性能,还是以刚才上述整形加法的例子,我们可以重写上述的函数如下。可以看到,由于SIMD可以一次处理一批数据,循环的次数衰减,性能能得到进一步提升。
voidint4addint4SIMD(int4a[],intb[],intres[]){for(inti=0;iN/SIMDLEN;i++)res[i..i+SIMDLEN]=SIMDADD(a[i..i+SIMDLEN],b[i..i+SIMDLEN];}
至此,openGauss数据库执行器技术章节结束,下一章将开启openGauss存储技术的学习,未完待续......