简介: 向量化引擎为PolarDB-X的表达式计算带来了显著的性能提升。

介绍

PolarDB-X是阿里巴巴自研的云原生分布式数据库,采用了计算-存储分离的架构,其中计算节点承担着大量的表达式计算任务。这些表达式计算涉及到SQL执行的各个环节,对性能有着重要的影响。为此PolarDB-X引入向量化执行引擎,为表达式计算带来了几十倍的性能提升。

传统数据库执行器的缺陷

现代数据库系统的执行引擎,大多采用一次计算一行数据(Tuple-at-a-time)的处理方式,并且需要在运行时对数据类型进行解析和判断,来适应复杂的表达式结构。我们称之为“标量(scalar)表达式”。这种方式虽然易于实现、结构清晰,但是当需要处理的数据量增大时,它具有显著的缺陷:

为了适应复杂的表达式结构,计算一条表达式往往需要引入大量的指令;对于行式执行来说,处理单条数据需要算子树重新进行指令解释(instruction interpretation),从而带来了大量的指令解释开销。据论文《MonetDB/X100: Hyper-Pipelining Query Execution》统计,在MySQL执行TPC-H测试集的 Query1 时,指令解释就耗费了90%的执行时间。

此外,在最初的Volcano结构设计中,算子内部逻辑并没有避免分支预测(branch prediction)。错误的分支预测需要CPU终止当前的流水线,将ELSE语句中的指令重新载入,我们将这一过程称为pipeline flush或pipeline break。频繁的分支预测错误会严重影响数据库的执行性能。

向量化执行系统

数据库向量化执行系统最早由论文《MonetDB/X100: Hyper-Pipelining Query Execution》提出,它有以下几个要点:

  1. 采用vector-at-a-time的执行模式,即以向量(vector)为数据组织单位。
  2. 使用向量化原语(vectorization primitives)来作为向量化算子的基本单位,从而构建整个向量化执行系统。原语中避免产生分支预测。
  3. 使用code generation(代码生成)技术来解决静态类型带来的code explosion(代码爆炸)问题。

向量化引擎为PolarDB-X的表达式计算带来了显著的性能提升。在下图中,横轴为向量大小,纵轴为吞吐量,不同标量表达式和向量化表达式的性能测试对比结果如下:

case表达式性能测试对比结果如下:

整体流程

PolarDB-X中,向量化表达式的执行分为以下几个阶段:

  1. 用户SQL经解析后,在validator中进行校验,推导和修正表达式的类型信息;这一阶段为向量化运算提供正确的、静态的类型信息;
  2. 在优化器形成执行计划之后,需要对表达式树进行表达式绑定,实例化对应的向量化原语,同时分配好向量下标,供运行时内存分配;
  3. 执行阶段,依据Volcano式的结构,自顶向下的触发执行向量化原语,并将向量作为运行时数据结构。

运行时结构

数据结构

在PolarDB-X向量化执行系统中,采用以下的数据结构来存放数据:

向量化表达式执行时,所有的数据都会存放在batch这一数据结构中。batch由许多向量(vector)和一个selection数组而组成。其中,向量vector包括一个存储特定类型的数值列表(values)和一个标识null值位置的null数组组成,它们在内存中都是连续存储的。null数组中的bit位以0和1来区分数值列表中的某个位置是否为空值。

我们可以用vector(type, index)来标识batch中一个向量。每个向量有其特定的下标位置(index),来表示向量在batch中的顺序;类型信息(type)来指定向量的类型。在进行向量化表达式求值之前,我们需要遍历整个表达式树,根据每个表达式的操作数和返回值来分配好下标位置,最后根据下标位置统一为向量分配内存。

延迟物化

selection数组的设计体现了延迟物化的思想,参考论文《Materialization Strategies in a Column-Oriented DBMS》。所谓延迟物化,就是尽可能地将物化(matrialization)这一过程后推,减少内存访问带来的开销。在执行表达式计算时,往往会先经过Filter表达式过滤一部分数据,再对过滤后的数据执行求值处理;每次过滤都会影响到batch中所有的向量。以上图中的batch为例,如果我们针对第0个向量设置 vector(int, 0) != 1这一过滤条件,假设vector(int, 0)中有90%的数据满足该过滤条件(选择率selectivity = 0.9),那么我们需要将batch中所有向量90%的数据重新物化到另一块内存中。而如果我们只记录满足该过滤条件的位置,存入selection数组,我们就可以避免这一物化过程。相应的,以后每次向量化求值过程中,都需要参考此selection数组。

向量化原语

向量化原语是向量化执行系统中的执行单位,它最大程度限制了执行期间的自由度。原语不用关注上下文信息,也不用在运行时进行类型解析和函数调用,只需要关注传入的向量即可。它是类型特定(Type-Specific)的,即一类原语只能处理特定类型。

向量化原语的主体是Tight-Loop的代码结构。在一个循环体内部,只需要进行取值和运算即可,没有任何的分支运算和函数调用。一个简单的向量化原语结构如下所示:

map_plus_double_col_double_col(int n,
double*__restrict__ res,
double*__restrict__ vector1, double*__restrict__ vector2,
int*__restrict__ selection)
{
if (selection) {
for(int j=0;j<n; j++) {
int i = selection[j];
res[i] = vector1[i] + vector2[i];
}
} else {
for(int i=0;i<n; i++)
res[i] = vector1[i] + vector2[i];
}
}

注:*左右滑动阅览

其运算过程利用了selection数组,逐步对向量进行取值、运算和存值,如下图所示:

向量化原语带来了以下优点:

  1. Type-Specific以及Tight-Loop的结构,大大减少了指令解释的开销;
  2. 避免分支预测失败和虚函数调用对CPU流水线的干扰,同时也能有利于 loop pipeline 优化【论文引用】
  3. 从向量中存取数据,有利于触发cache prefetch,减少cache miss带来的开销。

我们为各种标量化表达式提供相应的原语实现,从而完成从标量到向量化的转变。例如将加法运算 plus(Object, Object) 针对不同操作数类型生成原语,包括plus(double,double),plus(long, long)等。

短路求值

在向量化原语的基础上,我们可以进一步对分支运算(也称为控制流运算 Control-Flow)进行短路求值(short-circuit calculation)优化,提升表达式计算的性能。

例如,case 表达式由n个when表达式、n-1个then表达式、1个else表达式构成。对于表达式

select case when a > 1 then a * 2
when b > 1 then b * 2
else a * b

具有以下树形结构:

由于标量化表达式按照volcano结构编排,并提供了统一的next()的接口,case表达式必须执行完所有的子表达式a>1,a*2,b>1,b*2和a*b之后,将全部结果汇总到一起,最后做case语义处理。这种执行方式不能根据when表达式的处理结果及时终止计算过程,而是对全部子表达式无差别执行。

引入向量化执行器以后,我们可以设计短路求值来优化此问题,每一个子表达式需要被提供合适的selection数组,从而正确选择列中合适的位置来进行向量运算。

作者:君启

原文链接

本文为阿里云原创内容,未经允许不得转载

每次都需要解释大量指令?使用 PolarDB-X 向量化引擎的更多相关文章

  1. linux中sudo如何读取标准输入作为密码,避免每次都输入密码?

    需求描述: 今天想要在生产环境中,弄自动部署的脚本,但是现在呢,需要sudo权限,每次都要输入.就想看sudo如何能从标准输入读取密码. 操作过程: 1.原来的方法 [deployer@testvm ...

  2. Git push 时每次都需要密码的疑惑

    2015.1.13更新: 在本地搭建Git服务器时,也是有每次操作需要密码的情况. 是因为每次做推送动作时,Git需要认证你是好人.所以需要密码. 可以在 /home/username/.ssh/au ...

  3. 为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了

    为什么每个请求都要有用户名密码呢,那不是每次都要查询一下了,token,表示这个用户已经验证通过了,在token有效期内,只需要判断token是否有效就可以了

  4. LISTVIEW嵌套GRIDVIEW的一些处理(点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置)(对这篇文章的优化处理,不每次都new onItemClickListener)

    前几天写了点击GRIDVIEW的条目,能够显示他在LISTVIEW中的位置,当时的处理是在ListView的适配器里的GetView方法里每次都new GridView的onItemClickList ...

  5. git 设置不需要输入密码, 去除 fetch / pull 代码每次都需要输入密码的烦恼

    https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受https带来的极速 设置记住密码(默认15分钟): git config --global credenti ...

  6. 使用git提交到github,每次都要输入用户名和密码的解决方法

    使用git提交文件到github,每次都要输入用户名和密码,操作起来很麻烦,以下方法可解决,记录以下. 原因:在clone 项目的时候,使用了 https方式,而不是ssh方式. 默认clone 方式 ...

  7. 使用git提交代码到github,每次都要输入用户名和密码的解决方法

    自从使用git提交代码到github后,发现自己使用git的功力增长了不少,但也遇到不少问题.比如,使用git提交代码到github的时候,经常要求输入用户名和密码,类似这种: 网上有这么一种解决方法 ...

  8. Jupyter Notebook启动不会自动打开浏览器,每次都要自己打开浏览器输入网址

    今天在使用jupyter 时,已启动服务,但每次都需要手动去浏览器 输入网址才可以, 最好找了好久才解决了. 去cmd 命令窗口执行jupyter notebook --generate-config ...

  9. 【转载】win10解决设置默认打开方式不生效问题(双击每次都要选择默认打开程序)

    win10解决设置默认打开方式不生效问题(双击每次都要选择默认打开程序) 以下文章 部分选自 https://blog.csdn.net/shan165310175/article/details/8 ...

  10. 多个 gradle 文件夹 \.gradle\wrapper\dists\ 设置gradle不是每次都下载

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 设置gradle不是每次都下载 \.gradle\wrapper\dists\ ==== ...

随机推荐

  1. PBKDF2算法:保障密码安全的利器

    PBKDF2算法起源: PBKDF2(Password-Based Key Derivation Function 2)算法是一种基于密码的密钥派生函数,最初由RSA实验室的密码学家提出,用于从密码中 ...

  2. [noip2015]运输计划(LCA,二分)

    运输计划[做题笔记] 挺难绷的... 题意 概括:给定 \(n\) 个节点的树和 \(n-1\) 条边的权值,现在可以将一条边的权值改为 \(0\) .找出一条边,使得将这条边权值赋为 \(0\) 时 ...

  3. 说JS作用域,就不得不说说自执行函数

    一个兜兜转转,从"北深"回到三线城市的小码农,热爱生活,热爱技术,在这里和大家分享一个技术人员的点点滴滴.欢迎大家关注我的微信公众号:果冻想 前言 不得不吐槽,学个JS,这个概念也 ...

  4. 从零开始写 Docker(八)---实现 mydocker run -d 支持后台运行容器

    本文为从零开始写 Docker 系列第八篇,实现类似 docker run -d 的功能,使得容器能够后台运行. 完整代码见:https://github.com/lixd/mydocker 欢迎 S ...

  5. #Trie#洛谷 7717 「EZEC-10」序列

    题目 分析 考虑这些关系可以用若干个连通块表示,而可以用一个数异或边权表示, 那么每个连通块有一个生成树,而判断非树边是否合法即可, 那么问题就转换成有多少个数异或任意一个元素均不大于\(k\), 把 ...

  6. 2024最新AIGC系统ChatGPT网站源码,GPTs应用,Ai绘画网站源码

    一.前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型+国内AI全模型.本期针对源码系统整体测试下来非常完美,那么 ...

  7. OpenHarmony创新赛|赋能直播第五期

     OpenHarmony创新赛赋能直播课程即将再次与大家见面!本期基于之前的青蛙影院的UI界面设计的课程,介绍综合性APP的需求介绍和技术栈整合等内容.此外,课程同步赋能OpenHarmony创新赛, ...

  8. Git分支教程:详解分支创建、合并、删除等操作

    Git是一种强大的分布式版本控制系统,它的分支功能使得团队协作和代码管理变得更加灵活和高效.分支可以让开发人员在不影响主线开发的情况下进行并行开发和实验性工作.本篇博客将详解Git分支的创建.合并.删 ...

  9. Python 简介和用途

    什么是Python? Python是一种流行的编程语言,由Guido van Rossum创建,并于1991年发布. 它用于以下领域: 网页开发(服务器端) 软件开发 数学 系统脚本编写 Python ...

  10. 分析即服务(AaaS)是什么?终于有人讲清楚了

    随着世界数据领域的地位和规模的不断扩大,大数据.人工智能和云计算正在结合起来,以分析即服务的形式为企业提供急需的喘息机会 . 让我们简要回顾一下 2010 年以来发生的事情. 这十年来我们见证了许多技 ...