摘要:广义上讲就是指LLVM本身,它是一套用于开发编译前端与后端的工具套件,狭义上讲LLVM就是指整个编译套件的优化器及后端,而CLANG可以认为是C/C++的前端。

本文分享自华为云社区《GaussDB(DWS) 性能黑科技之LLVM技术解密》,作者:清道夫。

1.LLVM是什么?

LLVM这个名字最早源于底层虚拟机(Low Level VirTual Machine)的首字母缩写,但随着LLVM项目的演进,底层虚拟机的含义已经不再适用于LLVM。现在谈到LLVM,广义上讲就是指LLVM本身,它是一套用于开发编译前端与后端的工具套件,狭义上讲LLVM就是指整个编译套件的优化器及后端,而CLANG可以认为是C/C++的前端。

2.LLVM的优势?

传统编译器最常见的三阶段设计:

前端:解析源代码生成抽象语法树

优化器:根据优化规则对代码进行改善,相当于规则重写,例如消除冗余计算等

后端:将代码映射到目标指令集上,包括指令选择、寄存器分配和指令调度等。

GCC是一个完整的可执行文件,没有为其他语言的开发者提供代码重用的接口,灵活性不足。

LLVM也采用经典的三段式设计,但与传统编译器最大的不同就是针对不同语言都提供了同样一种中间表示IR以及模块化的后端(MCJIT模块可以支持JIT编译)。

3.DWS为什么要使用LLVM?

为具体的查询生成定制化的机器码代替通用的函数实现,并尽可能的将数据存储在CPU的寄存器中:

(1)解决条件逻辑冗余的问题,需要用到JIT(jiust-in-time)编译技术,LLVM天然支持JIT技术。

(2)减少大量虚函数调用

(3)改善数据调用,将数据尽可能的从内存加载到cache上

(4)发挥通用硬件平台的扩展指令集功能,例如SSE4.2

一个简单的例子,可以参照下面的优化前伪代码片段:

(1)首先是优化前的代码片段(a):
可以看到,在物化tuple的过程中,需要根据不同的列属性判断其偏移量,并调用相应的解析函数。

(2)借助LLVM使用JIT编译技术后,可以生成如下优化后的伪代码片段(b):
其中偏移量已经被提前计算出来,且无需判断列属性既可以调用对应的解析函数。减少了偏移量的重复计算及类型的重复判断。

优化前的代码(a):

void MaterializeTuple(char *tuple)
{
for (int i = 0; i < num_slots; ++i) {
char *slot = tuple + offset[i];
switch(types[i]) {
case BOOLEAN:
*slot = ParseBoolean();
break;
case INT:
*slot = ParseInt();
break;
case FLOAT:...
case STRING:...
// etc.
}
}
}

优化后的代码(b):

void MaterializeTuple(char *tuple)
{
*(tuple + 0) = ParseInt();
*(tuple + 4) = ParseBoolean();
*(tuple + 5) = ParseInt();
}

4.如何使用LLVM

在DWS中,涉及LLVM的GUC参数有两个:

(1)enable_codegen:总开关,用于控制是否开启codegen,默认为on

(2)codegen_cost_threshold:使用处理行数控制是否开启codegen,默认门槛值为10000。

目前DWS中并非是通过计划代价去控制是否开启codegen,而是通过处理行数来控制的。此处10000是通过实验验证得出的优化值,不建议将此门槛值设置的过低,因为代码执行过程中的即时编译是有代价的。

简单示例:

test=# create table llvm_test(a int,b int)with(orientation=column);
NOTICE: The 'DISTRIBUTE BY' clause is not specified. Using 'a' as the distribution column by default.
HINT: Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
CREATE TABLE
test=# insert into llvm_test values(generate_series(1,10),generate_series(1,10));
INSERT 0 10
test=# show enable_codegen;
enable_codegen
----------------
on
(1 row) test=# show codegen_cost_threshold;
codegen_cost_threshold
------------------------
10000
(1 row) test=# set codegen_cost_threshold=0; --为了简化,将门槛值设置为0
SET test=# set enable_fast_query_shipping=off; --关闭FQS,便于打印DN执行信息
SET
test=# explain performance select * from llvm_test where b<10; --简单表达式会使用codegen
Datanode Information (identified by plan id)
---------------------------------------------------------------------------------------------------------------------
1 --Row Adapter
(actual time=14.929..15.393 rows=9 loops=1)
(CPU: ex c/r=5783, ex row=9, ex cyc=52048, inc cyc=46174324)
2 --Vector Streaming (type: GATHER)
(actual time=14.920..15.372 rows=9 loops=1)
(Buffers: 0)
(CPU: ex c/r=5124697, ex row=9, ex cyc=46122276, inc cyc=46122276)
3 --CStore Scan on public.llvm_test
LLVM Optimized
datanode1 (actual time=0.094..0.141 rows=9 loops=1) (filter time=0.002) (RoughCheck CU: CUNone: 1, CUSome: 9)
datanode1 (Buffers: shared hit=26 dirtied=1)
datanode1 (CPU: ex c/r=41430, ex row=10, ex cyc=414306, inc cyc=414306)

此处仅截取部分执行信息,在Datanode Information中的扫描算子中有LLVM Optimized信息,代表已经使用了JIT编译。

如果想查看LLVM JIT编译的时间耗时,可以借助GUC参数analysis_options进行设置后,执行对应查询语句,在User Define Profiling中就可以看到LLVM的编译时间。

test=# set analysis_options='on(LLVM_COMPILE)';
SET
test=# explain performance select * from llvm_test where b<10;

此处仍使用之前用例的设置,不再赘述。

                      User Define Profiling
----------------------------------------------------------------
Plan Node id: 2 Track name: coordinator get datanode connection
coordinator1: (time=0.015 total_calls=1 loops=1)
Plan Node id: 3 Track name: load CU description
datanode1 (time=0.061 total_calls=10 loops=1)
Plan Node id: 3 Track name: min/max check
datanode1 (time=0.008 total_calls=10 loops=1)
Plan Node id: 3 Track name: fill vector batch
datanode1 (time=0.032 total_calls=9 loops=1)
Plan Node id: 3 Track name: apply projection and filter
datanode1 (time=0.005 total_calls=9 loops=1)
Plan Node id: 3 Track name: LLVM Compilation
datanode1 (time=11.024 total_calls=1 loops=1)
Plan Node id: 3 Track name: fill later vector batch
datanode1 (time=0.000 total_calls=9 loops=1)

其中LLVM Compilation即为LLVM的即时编译的时间代价。此处编译的时间代价通常会与SQL执行流程的复杂程度成正比关系,在实际的调优实践中可以结合此数据对处理数据行数的门槛值做进一步的调整。

5.LLVM适用场景

目前LLVM仅支持DN上且是列存向量化执行路径的查询作业,其支持的表达式及算子如下:

  • 支持LLVM的表达式

    1. Case…when… 表达式
    2. In表达式
    3. Bool表达式 (And/Or/Not)
    4. BooleanTest表达式 (IS_NOT_KNOWN/IS_UNKNOWN/IS_TRUE/IS_NOT_TRUE/IS_FALSE/IS_NOT_FALSE)
    5. NullTest表达式 (IS_NOT_NULL/IS_NULL)
    6. Operator表达式
    7. Function表达式 (lpad, substring, btrim, rtrim, length)
    8. Nullif表达式

    表达式计算支持的数据类型包括bool, tinyint, smallint, int, bigint, float4, float8, numeric, date, time, timetz, timestamp, timestamptz, interval, bpchar, varchar, text, oid。

    仅当表达式出现在向量化执行引擎中Scan节点的filter、Hash Join节点中的complicate hash condition、hash join filter、hash join target, Nested Loop节点中的filter、join filter, Merge Join节点的merge join filter, merge join target, Group节点中的filter表达式时,才会考虑是否使用LLVM动态编译优化。

  • 支持LLVM的算子:

    1. Join :HashJoin/SonicHashJoin
    2. Agg :HashAgg
    3. Sort

    其中HashJoin算子仅支持Hash Inner Join,对应的hash cond仅支持int4、bigint、bpchar类型的比较;HashAgg算子仅支持针对bigint、numeric类型的sum及avg操作,且group by语句仅支持int4、bigint、bpchar,text,varchar,timestamp类型操作,同时支持count(*)聚集操作。Sort算子仅支持对int4,bigint,numeric,bpchar,text,varchar数据类型的比较操作。除此之外,无法使用LLVM动态编译优化,具体可通过explain performance工具进行显示(如上用例所示)。

想了解GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技,后台还可获取众多学习资料哦~

点击关注,第一时间了解华为云新鲜技术~

解密数据仓库LLVM技术神奇之处的更多相关文章

  1. 全面解密QQ红包技术方案:架构、技术实现、移动端优化、创新玩法等

    本文来自腾讯QQ技术团队工程师许灵锋.周海发的技术分享. 一.引言 自 2015 年春节以来,QQ 春节红包经历了企业红包(2015 年).刷一刷红包(2016 年)和 AR 红包(2017 年)几个 ...

  2. BAT解密:互联网技术发展之路(5)- 开发层技术剖析

    BAT解密:互联网技术发展之路(5)- 开发层技术剖析 1. 开发框架 在系列文章的第2篇"BAT解密:互联网技术发展之路(2)- 业务怎样驱动技术发展"中我们深入分析了互联网业务 ...

  3. php isset( $test ) 的神奇之处。

    很久一段时间没更新博客了,由于近段时间一直在忙 挑战杯 的项目,所以没怎样把一些总结放上来.这次,总结下 php 的一个 函数 : boolean isset($test), 返回值:boolean类 ...

  4. python的for循环的神奇之处

    python的for循环太神奇了: 你可以编写这样的语句: for i in range(10) : j= i**2 print(j) 你也可以编写这样的语句: with open('/path/to ...

  5. 【转载】如何直观的理解 O(logn) 时间复杂度的神奇之处

    转自 https://blog.csdn.net/u012814856/article/details/83010082 一.引言最近在极客时间上订阅了<数据结构与算法>的课程,其中王争老 ...

  6. Java toString()方法的神奇之处

    Java 手册 toString(String类中) public String toString() 返回此对象本身(它已经是一个字符串!). 指定者: 接口 CharSequence 中的 toS ...

  7. 单元测试框架怎么搭?快来看看新版Junit5的这些神奇之处吧!

    为什么使用JUnit5 JUnit4被广泛使用,但是许多场景下使用起来语法较为繁琐,JUnit5中支持lambda表达式,语法简单且代码不冗余. JUnit5易扩展,包容性强,可以接入其他的测试引擎. ...

  8. 一起来看看java并发中volatile关键字的神奇之处

    并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volati ...

  9. 漫谈 Google 的 Native Client(NaCl) 技术(二)---- 技术篇(兼谈 LLVM)

    转自:http://hzx5.blog.163.com/blog/static/40744388201172531637729/ 漫谈 Google 的 Native Client(NaCl) 技术( ...

  10. 解密国内BAT等大厂前端技术体系-携程篇(长文建议收藏)

    1 引言 为了了解当前前端的发展趋势,让我们从国内各大互联网大厂开始,了解他们的最新动态和未来规划.这是解密大厂前端技术体系的第四篇,前三篇已经讲述了阿里.腾讯.百度在前端技术这几年的技术发展. 这一 ...

随机推荐

  1. 手撕Vuex-Vuex实现原理分析

    本章节主要围绕着手撕 Vuex,那么在手撕之前,先来回顾一下 Vuex 的基本使用. 创建一个 Vuex 项目,我这里采用 vue-cli 创建一个项目,然后安装 Vuex. vue create v ...

  2. 基于Echart的前端可视化

    GitHub 上有许多关于低代码自助可视化的项目,前端使用 Vue 和 ECharts 的示例.以下是一些可能符合你要求的项目: DataV: 项目链接:DataV 描述:DataV 是一款基于 Vu ...

  3. 网页全终端h5浏览器视频流解决方案RTSP/FLV/HLS

    背景 项目上需要基于视频巡检,在线勘查填写定制表单,降低巡检成本. 本文着重讲前端部分视频流展示解决方案. 调研 流媒体(streaming media)是指将一连串的媒体数据压缩后,经过网上分段发送 ...

  4. Redis项目搭建

    Redis项目搭建 Redis下载 搭建redis首先需要下载Redis,可是Redis官方并没有Windows安装,好在网上从不缺大牛,Github上可以找到Redis的Windows版 下载地址: ...

  5. Android 使用 ContentProvider 简单操作数据库

    ContentProvider 可以用来原生读写 Android 自带的数据库 SQLite. 使用 Studio 创建一个 ContentProvider, 名字叫 TestContentProvi ...

  6. 删除当前文件夹不是.vue文件,电脑命令符

    ::-----------------------------------------@echo offsetlocal EnableDelayedExpansionset _thisFilePath ...

  7. 如何使用Tampermonkey开发并使用一个浏览器脚本

    准备工作 简介 Tampermonkey 是一款强大的浏览器扩展,它允许您定制网页的行为,改变和优化网页的展示方式或者功能以满足个人需求.通过编写自定义脚本,您可以实现许多有趣的功能,从自动化任务到改 ...

  8. 有一种浪漫,叫接触Linux

    ​大家好,我是五月. 嵌入式开发 嵌入式开发产品必须依赖硬件和软件. 硬件一般使用51单片机,STM32.ARM,做成的产品以平板,手机,智能机器人,智能小车居多. 软件用的当然是以linux系统为蓝 ...

  9. StackGres 1.6 数据库平台工程功能介绍以及快速上手

    StackGres 1.6 数据库平台工程功能 声明式 K8S CRs StackGres operator 完全由 Kubernetes 自定义资源管理.除了 kubectl 或任何其他 Kuber ...

  10. Oracle数据库卸载器 - 开源研究系列文章

    今天无事,把网上搜到的Oracle数据库卸载器的软件更新到C#的Winform界面的操作上. 1. 程序目录: 与笔者的其它软件类似,目录如下: 2. 使用的类: 这里主要使用了一个处理函数: 3. ...