写点什么

数据库内核杂谈 (十五): 执行器之 code generation vs vectorized execution

  • 2021-01-27
  • 本文字数:3668 字

    阅读完需:约 12 分钟

数据库内核杂谈(十五): 执行器之code generation vs vectorized execution

本文是数据库内核系列文章之一。


从本文开始,我们一起来学习一下数据库引擎执行器中非常出名的两种优化方式,code generation(代码生成;以下简称 code-gen)和 vectorized execution(向量计算;以下简称 vec-exec)。为什么说学习呢,因为自己确实也没做过。所有的学习资料也是阅读一些技术博客和技术论文,我会仔细阅读,争取可以用深入浅出的语言写出来。

 

说到 code-gen 和 vec-exec,这两个概念出自两篇非常有名的文章。code-gen 出自 Thomas Neumann 的文章:Efficiently Compiling Efficient Query Plans for Modern Hardware;vec-exec 出自 MonetDB/X00: Hyper-Pipelining Query Execution。MonetDB 的作者之一是 Marcin Zukowski。他的博士毕业论文就是详细阐述了 vec-exec(目前暂定计划是,仔细阅读一下他的博士毕业论文,然后输出一到两期)。他也是目前超级火热的 Snowflake 数据库公司的创始人之一,Snowflake 的执行器就是基于 vec-exec 的。讲到这,我得说一个没吃到葡萄,觉得葡萄特别酸的故事:早先在数据库会议上见过 Marcin 几次,是个非常 nice 的人。当时 Snowflake 也刚开始做没多久吧。Marcin 说,他想招做 optimizer 的。我说,我就是啊。Marcin 说,我想招个女性。。。我说,为啥。。。因为 diversity?他说,不是,因为现在 optimizer 组全是女性,他希望能保持 optimizer 组全女性阵容(强迫症晚期患者)。然后我还去问了 Lyublena,但是 Lyublena 当时也刚加入 Datometry,没有去。我,因为性别,错过了一次财务自由的机会。

 

后来,Andy Pavlo(CMU 数据库教授)把 Thomas 和 Peter(MonetDB 另一位作者)组在一起,写了一篇 survey paper: everything you always wanted to know about compiled and vectorized queries but were afraid to ask(不得不说,Andy 的所有论文,名字起的都特别网红)。今天的内容,咱们从读懂这篇 survey paper 开始。

 

绝大部分的现代数据库的执行器引擎都是基于 code-gen 或者 vec-exec 的。  有意思的是,它们都为了提升性能而生,但是却走了不同的两条路。目前,两种技术都被成熟应用到数据库系统中。但是,至今为止,也没能很客观地去比较这两种技术的好坏。因为,对于一个数据库系统而言,一般只会选择点选一个技能树,然后,就沿着这条技能树发展,所有的发展都是围绕这个技术的。如果比较不同的数据库,由于不可控的因素太多了,很难可观地比较这两种技术。这也是 Andy 他们写这篇文章的初衷:为了可以公正地比较这两种技术,专门在自己开源的数据库上实现了两种技术,而且,尽量做到客观,公正,使用相同的算法和类型模型。

 

杂谈第四章,介绍执行模式的时候,我们介绍过传统的执行器执行模式,Volcano-style 模型(火山模型)。Volcano-style 的优点在于,非常的 general,容易扩展和理解,实现也相对容易。并且,当还处于读写磁盘是主要性能瓶颈的年代,并没有察觉,这个模型会对性能造成太多损失。但现在不同了,所有硬件的性能都提升了不止几个数量级。数据库也在为了追求极致性能不断优化。 普通硬盘慢,那就用 SSD,再不行,就纯 in-memory 数据库。行存(row-store)对于读海量数据的 OLAP 语句来说没有优势,那就改为列存(column-store)。在执行器这方面,就衍生出了两种技术,code-gen 和 vec-exec。主流的数据库几乎都使用了其中一种来作为执行器。用 vec-exec 的有,DB2-BLU,Columnar SQL Server, 以及 QuickStep 等等. 而 code-gen 则有 Apache Spark 以及 Peloton 等等,包括 Pivotal 的 GPDB 也有 code-gen 模式。

 

下面,先简单介绍下这两项技术。

Vec-exec


Vec-exec,类似于 Volcano-style,也是基于拉模式的方式来处理。区别在于,next 方法不再只返回一个 tuple,而是一个 batch 的 tuples。因此在处理的时候,可以批量一起处理一波 tuples。读者可能会有疑问,诚然,每一次拉取了一波 tuples 一起处理,确实是节省了总共调用 next 的次数。但是说到底,总共要处理的 tuple 是没有变化的。为什么 vec-exec 能带来可观的性能提升呢?其实是基于硬件的提升:SIMD: single instruction multi-data。即,单个指令直接作用到多个数据,比如,可以同时计算多个 vector 里数据的 hash 值。因此,vec-exec 更适合配合 column-store。

 

Code-gen


而 code-gen,虽然也是利用硬件带来的性能提升。相比 vec-exec,使用了完全不同的模式。虽然还是有 operator,但 operator 不再是相互独立地处理数据,而是把所有的 operator 当成一个整体,然后生成出一段代码来实现整个逻辑:为每个 query 都会单独去生成一段代码来实现。可能读者会有相同的疑问,性能提升是从何说起。举个例子,大家都听说过 Google 大神 Jeff Dean 的故事。传言 compiler 从来不给他优化提示,因为他手写的代码比 compiler 更优化。你大致可以想象,code-gen 呢就是,对于每个语句,都找一个 Jeff Dean 一样的高手,根据数据的类型,数据的大小,然后结合硬件 CPU,寄存器,内存等的信息,手写一段代码来实现这个语句。不需要兼顾 general 属性,因此可以更高效。而传统的 volcano-style,因为兼顾 general,因此代码本身是并不优化的。而且,code-gen 生成的代码可以进一步通过编译优化生成性能更高的机器代码(比如,通过 LLVM)。

 

稍微展开一点,怎么才可以生成出性能更高的机器代码呢?一个衡量的标准就是运行这个语句,总共的 CPU 指令更少。如何才能生成 CPU 指令更少的机器代码呢。我也不是专家,阅读文章后,这是我自己的理解。CPU 运行的时候,需要通过和寄存器交互来处理数据,而把数据从内存换到寄存器中是需要消耗 CPU 指令的。那一个简单的思想就是,如果生成的代码,可以尽可能地提高寄存器的利用率,即,使得连续的 CPU 指令都能 hit 寄存器中的数据,而不是消耗在不停地交换内存数据中,这样的代码就更高效。

 

什么传统的 volcano-style 对 CPU 或者寄存器不是很友好。原因之一就在于设计模式非常 general(好处在于比较容易实现)但劣势在于,为了追求通用性,会增加额外方法调用的开销,并且大大增加了寄存器 miss 的可能性。为什么这么说呢。来看一个示例。假设处理下列的语句

 

SELECT  COL1 + 10 as NEW_COL1, COL2, COL3, COL4, ...., COL20 FROM  TABLE1WHERE   COL1 > 10;
复制代码

 

因为 tuple 是一条一条处理的,因此,第一个 filter operator 会不停地从 TableScan 读取数据,然后判断是否 COL1>10。判断完了后,上层的 projection operator 会得到这个 tuple,然后处理 COL1+10。但是,这边多了一次方法调用,是有开销的。并且,假设 tuple 过长,极有可能数据就被从寄存器换出,执行加法操作还要再多一条指令将 COL1 读进寄存器。


那 code-gen 可以怎么做呢?对于每一个 COL1,可以把 COL1+10 的这个操作合并到 filter operator 一起,这样,就可以保证寄存器 hit,而且,生成的 code 可以直接是一个大 for 循环,完全去掉不必要的方法调用。

 

论文种给出的 HashJoin 示例比较


文中给出 HashJoin 的 code-gen 和 vec-exec 的代码比较。


可以看到,code-gen 的做法就是先读取用来构建 hash 表一侧的所有数据,构建 hash 表。然后读取右侧 table 来判断 hash 条件是否某满足。从这个实现上看,也没有特别神奇的地方。但总结来说,去掉了一个一个 tuple 处理的模式,对 cache hit 更有利;并且,去掉了 next 方法调用的额外开销。而且,对于这类 for 循环,通常编译器还能更好地做优化来生成机器代码。



上图种 vec-exec 所用的代码,在构建 hash 表时,代码类似,因此省略了。在 hash probe 时,和单个 tuple 比,每次读取一个 batch 的 tuples,来处理,也没有特别多神奇的地方。

比较结果总结


文中用了大段来陈述比较结果,并且从多个维度进行了比较。例如,分别考虑了单线程模型,多线程模型的实现;不同的硬件场景;是否使用 SIMD。比较的维度也有 CPU cycles,指令数,L1 Cache miss, LLC (Last-level cache) miss, branch miss 等等。我在这就不一一复述了,相信大家也不是特别感兴趣。


翻译一下最后的总结:


1)code-gen 对于计算复杂性更高的语句来说,更有优势,因为可以通过代码优化来提高寄存器利用率,从而需要更少的指令来执行整个计划。

2)对于并行处理数据 SIMD 来说, vec-exec 更适合。

3)对于多 CPU 环境,两项技术都可以 scale。

4)对于不同硬件环境,两项技术也是伯仲之间。

 

用一句话而言,两项技术都可以提升性能,侧重点不同,语句的不同,带来的性能提升也不同。

 

说一下我自己肤浅的理解。首先,code-Gen 的实现更复杂。特别是要实现高质量的代码,毕竟,你不会真有一个 Jeff Dean 在后台给你写代码,相当于实现了一个 compiler。另外要提的是,Code-gen 的本身从 query 到 generated code 是需要花时间的。这个时间,通常和 Query Optimization 的时间合并在一起了,但肯定是比普通的优化时间更长的。 此外,还要把这个生成的 code 再翻译成机器代码。文中提到了用 LLVM:LLVM 会进一步对代码优化,生成机器码,时间复杂度基本和代码量成线性。

 

我个人觉得,code-gen 应该会比 vectorized execution 性能更高一些,如果实现的好的话。其实大家可以想一下,code-gen 完全可以生成类似 vec-exec 的代码,如果在某些场景下,后者性能更高的话。


2021 年的愿景之一是做更多对于技术和管理的输出,如果想要和我更多交流,欢迎关注我的知识星球:Dr.ZZZ 聊技术和管理

2021-01-27 23:027718

评论 5 条评论

发布
用户头像
pull跟push是不能兼容的,但是code-gen跟vectorization肯定是可以兼容的吧。c++写的olap数仓做compiler auto-vectorization不就是code-gen+vectorization么?
2022-07-25 10:21
回复
用户头像
作者留言:2022年1月1日,祝大家新年快乐。不知不觉,数据库内核杂谈又陪伴大家一年(虽然。。还是不可避免地拖更了)。今年的new year resolution,希望创建一个群,和大家一起交流。加我微信 81211430(请备注数据库内核杂谈,谢谢),我会建群。期待和大家交流。
2022-01-01 12:31
回复
用户头像
目前 SparkSQL 貌似就在同时做 codegen 以及向量化,老师有了解过怎么结合起来的吗?
2021-09-02 08:35
回复
用户头像
OTAP场景下向量模型能起到优化作用吗
2021-04-08 00:28
回复
当然,看看cockroach跟tidb做的vectorization的工作即可
2022-07-25 10:11
回复
没有更多了
发现更多内容

兆骑科创创业赛事活动路演,高层次人才引进平台

兆骑科创凤阁

绝对最直白的MySQL MVCC机制总结,免费拿走

知识浅谈

开源 8月月更

浅聊组合函数

掘金安东尼

前端 函数编程 8月月更

想做好分布式架构?这个知识点一定要理解透彻

王小凡

Java 程序员 分布式 高并发

一文搞懂│php 中的 DI 依赖注入

设计模式 依赖注入 8月月更 高级编程

A tour of gRPC:06 - gRPC client straming 客户端流

BUG侦探

gRPC RPC

兆骑科创双创服务平台,创业赛事活动,投融资对接平台

兆骑科创凤阁

大咖说·图书分享 | Serverless工程实践:从入门到进阶

大咖说

Serverless 工程实践

大数据培训机构大概要花费多少钱

小谷哥

史上最全!47个“数字化转型”常见术语合集,看完秒懂~

优秀

数字化转型 数字化业务转型

国内IT市场还有发展吗?有哪些创新好用的IT运维工具可以推荐?

行云管家

云计算 多云管理 IT运维 云管理

Kubernetes资源编排系列之三: Kustomize篇

阿里云大数据AI技术

运维‘

《数字经济全景白皮书》银行业智能风控科技应用专题分析 发布

易观分析

金融 银行 数字经济全景白皮书

Apache APISIX 2.15 版本发布,为插件增加更多灵活性

API7.ai 技术团队

开源 后端 API网关 APISIX 网关

2022年值得尝试的7个MQTT客户端工具

EMQ映云科技

物联网 IoT mqtt 客户端 8月月更

Python 教程之输入输出(2)—— 输入和输出

海拥(haiyong.site)

Python 8月月更

开篇-开启全新的.NET现代应用开发体验

MASA技术团队

.net 云原生 后端

华为研究院19级研究员几年心得,终成趣谈网络协议文档,附大牛讲解

冉然学Java

数据库 编程 微服务 网络协议 java\

大数据培训如何部署一个健壮的Airflow

小谷哥

这几年让你大呼惊人的AI应用,都离不开这项技术

小红书技术REDtech

人工智能 自然语言处理 nlp 自然语言 自然语言理解

IDO预售DAPP系统开发(NFT挖矿)

薇電13242772558

dapp

玉溪卷烟厂通过正确选择时序数据库 轻松应对超万亿行数据

TDengine

数据库 tdengine 时序数据库

Python字体反爬之乐居字体反爬,一文看懂,一文学会

梦想橡皮擦

Python 爬虫 8月月更

语音直播系统——做好敏感词汇屏蔽打造绿色社交环境

开源直播系统源码

软件开发 语聊房 直播系统源码 语音直播系统

中科驭数等单位牵头发布行业首部DPU评测方法技术白皮书

硬科技星球

并发模型和I/O模型介绍

C++后台开发

后端开发 I/O模型 C/C++后台开发 C/C++开发 并发模型

AI+BI+可视化,Sugar BI架构深度剖析

百度Geek说

架构 数据

快速搞懂Seata分布式事务AT、TCC、SAGA、XA模式选型

知识浅谈

开源 8月月更

参加前端培训后程序员能找到工作吗?

小谷哥

浅析PM2实用入门指南

青年码农

Node pm2

开源一夏 | 不会吧,十分钟就能上手Prometheus与Grafana监控SpringBoot项目

知识浅谈

开源 8月月更 SpringBoot实战

数据库内核杂谈(十五): 执行器之code generation vs vectorized execution_数据库_顾仲贤_InfoQ精选文章