在8.1节的概要介绍中提到了执行器的数据流动模式:控制流向下、数据流向上。传统的执行引擎数据流遵循一次一元组的传输模式,而向量化引擎将这个模型改成一次一批元组的模式,这种看似简单的修改却带来巨大的性能提升,如图8-6所示。
图8-6 单个元组与向量化元组对比
其中的主要提升原因可以应对上面介绍的CPU架构里影响性能的几个关键因素。
一次一元组的函数模型在控制流的调动下,每次都需要进行函数调用,调用次数随着数据增长而增长,而一批元组的模式则大大降低了执行节点的函数调用开销,如果我们设定一次一批的数量为1000,函数调用相对于一次一元组能减少三个数量级。
一次一批元组的模式在内部实现通过数组来表达,数组对于CPU的预取非常友好,能够让数组在后续的数据处理过程中,大概率能够在CACHE中命中。
比如对于下面这个简单计算两个整形加法的表达式函数(其代码仅为了展示,不代表真实实现),下面展示了一次一元组和一次一批元组的两种写法
一次一元组的整形加法
int int4addint4(int4 a, int b)
{
Return a+b;
}
一次一批元组的整形加法
void int4addint4(int4 a[], int b[], int res[])
{ for(int i = 0; i < N; i++)
res[i] = a[i] + b[i];
}
一次一批元组的这个计算函数,因为CPU CACHE的局部性原理,数据和指令的cache命中率会非常好,极大提升处理性能。
一次一批元组的数据数组化的组织方式为利用SIMD特性带来了非常好的机会,SIMD能够大大提升在元组上的计算性能,还是以刚才上述整形加法的例子,我们可以重写上述的函数如下。可以看到,由于SIMD可以一次处理一批数据,循环的次数衰减,性能能得到进一步提升。
void int4addint4SIMD(int4 a[], int b[], int res[])
{
for(int i = 0; i < N/SIMDLEN; i++)
res[i..i+SIMDLEN] = SIMDADD(a[i..i+SIMDLEN], b[i..i+ SIMDLEN];
}