万物互联的态势下数据量的激增使得“如何提升数据处理性能”成为各家数据库共同面临的挑战。作为编译优化技术的代表基于LLVM的CodeGen技术能为每个查询生成定制的机器码替代原本的通用函数减少实际查询时冗余的条件逻辑判断、虚函数调用并提高数据局域性从而达到提升查询整体性能的目的成为数据库性能优化的一项重要技术。LLVM能在分析类场景中给用户带来较大的收益也能在特定的交易性场景中给用户带来一定的收益。接下来详细解读一下LLVM技术在GaussDB中的应用吧。LLVM和数据库LLVMLow Level Virtual Machine是一款流行的开源编译器框架是CodeGen生成源代码的工具技术的事实标准被广泛运用于数据库如KESAnalyticDBGaussDB、大数据如Spark、AI平台如tensorflow等领域用于提升数据处理的性能。在没有引入LLVM这类CodeGen技术之前数据库会使用通用的处理逻辑来处理数据但通用逻辑“笨重”递归、封装、类型判断转换的代码实现方式存在虚函数开销、缓存使用率低下、对指令集不敏感等性能短板。引入LLVM之后可以为具体的查询生成定制化的机器码并尽可能的将数据存储在CPU的寄存器中进一步加快计算的速度LLVM天然支持JIT该技术可以解决条件逻辑冗余的问题减少大量的虚函数调用将数据尽可能地从内存加载到Cache上LLVM做了很多自动矢量化的工作比如下图左侧是通用代码右侧是CodeGen之后的代码。CodeGen根据实际情况消除了不必要的循环和判断。▲ 图1 通用性处理逻辑和LLVM代码示意另外LLVM技术可以有不同的实现粒度。比如可使用LLVM加速表达式计算或再进一步将多个算子融合编译成定制的机器码或将自定义函数、存储过程等编译成定制的机器码。▲ 图2 LLVM的实现粒度数据库在执行引擎中运用LLVM技术提升SQL的执行速度。如下图所示▲ 图3 LLVM技术运用于执行引擎LLVM适用场景LLVM对所有类型的SQL都会有收益吗答案是否定的。因为执行实时编译本身需要耗费一定的时间简单表达式能做到毫秒级复杂情况在百毫秒级对于查询本身耗时较少的场景加入LLVM反而会导致性能劣化。因此目前LLVM在OLAP/HTAP分析型业务场景中收益较大有着广泛应用而在OLTP交易型业务场景中则相对没有那么广泛。LLVM在OLTP中就一定没有收益吗答案同样是否定的。找对场景一样有收益。比如根据ISPRAS 2017年发表的实验结果jit-compiling sql queries in postgresql using llvm可知pgbench测试下OLTP场景中简单的查询加上JITJust-in-time及时编译LLVM天然支持扩展没有带来性能的提升甚至将TPS事务数/秒从21.8降低到了7.8。但是在Prepared queryplan cached的情况下叠加 JIT技术之后将TPS从20.7提升到了43性能上有了两倍的提升。▲ 图4 简单查询、CodeGen流程、Plancache和“Plancache CodeGen”流程的性能对比GaussDB中的LLVM1. LLVM在华为应用于数据库的时间线华为云数据库在LLVM上的研究还是非常超前的。早在2015年华为就作为某流行开源数据库社区的全球开发者大会的赞助商在会上发表的动态编译Go Faster with Native Compilation演讲引起了很大的反响。当时社区领袖Josh Burkus在其博客里面用一节篇幅专门详细介绍了华为动态编译的议题。▲ 图5 2015年社区领袖Josh Burkus介绍华为的动态编译议题在2017年华为在面向OLAP场景的数据库内核中突破了LLVM动态编译技术并在多个运营商、金融证券的POC项目中帮助客户提升数据处理性能同时在软件开发过程中充分模块化、通用化接口设计将LLVM同年落地到面向OLTP的数据库设计中。目前GaussDB数据库对于LLVM也在不断的演进开发。2. GaussDB LLVM实现简析GaussDB针对向量化引擎主要用于分析场景、行存主要用于交易场景都实现了CodeGen。如下图所示从代码模块层次来看1) GaussDB通过API接口层封装处理了LLVM环境、资源、基本元素等。2) GaussDB在CodeGen层调用API接口进行了不同粒度的实现。3) GaussDB在执行引擎侧根据情况使用CodeGen技术进行性能优化。▲ 图6 GaussDB LLVM 模块层次图GaussDB启动后会进行LLVM的初始化工作检查CPU对CodeGen的支持情况并进行环境初始化。在执行启动阶段以表达式为例程序会判断当前表达式是否可JIT是的话则会进行IR函数的生成和生成定制机器码及原本表达式执行函数的入口替代工作。在实际执行过程中运行处理函数该函数已经在上一阶段进行了入口替代进行实际执行工作。在执行结束后的清理阶段释放LLVM相关资源。▲ 图7 GaussDB CodeGen编译执行流程简图GaussDB使用了阈值codegen_cost_threshold来估算当前查询使用LLVM技术是否能带来收益。如果处理数据的规模大于该阈值后才会继续使用LLVM技术进行相关处理。该阈值代表行数也可以理解成处理数据的规模默认值为100000行可以调节。在OLAP场景中GaussDB在判断是否能够对于一个算子进行CodeGen后如数据类型算子类型判断等开始生成对应的IR bytecode片段之后MCJIT模块会调用生成的LLVM Module单元进行执行。在OLTP场景中GaussDB则会在Plan Cache场景下结合CodeGen框架通过缓存机器码的方式节省下编译生成中间语言IR Func以及优化成机器码的时间整个过程是异步的。因此在大量重复查询的场景下后续的查询也会因为LLVM技术而受益。另外为了避免行数估计错误而选择CodeGen导致性能劣化GaussDB还研发了当前业界独有的异步编译功能即在查询语句确定要使用CodeGen的时候将编译工作转交给后台线程工作线程在JIT函数编译完成前继续使用原始执行逻辑执行编译完成后再替换成JIT函数执行。3. GaussDB LLVM支持加速的场景支持LLVM的表达式行存表达式计算支持的数据类型不受限制。在向量化执行引擎中仅当表达式出现在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动态编译优化。在行执行引擎中除一次性的表达式计算外会考虑为所有算子的filter和Targetlist表达式都使用LLVM动态编译优化。支持LLVM的算子Join HashJoin仅向量化执行引擎支持Agg HashAggSort仅向量化执行引擎支持其中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工具进行显示。4. GaussDB LLVM使用建议GUC参数enable_codegen控制LLVM特性的打开和关闭。目前数据库内核侧默认打开。codegen_cost_threshold使用处理行数控制是否开启codegen默认为10000。10000是通过实验验证得出的优化值不建议将此值设置的过低。另外在开启LLVM特性的前提下建议在允许的条件下尽可能设置较大的work_mem如果出现大量下盘则建议关闭LLVM动态编译优化。用户可通过analysis_options为on(LLVM_COMPILE)执行对应查询语句在User Define Profiling中就可以看到LLVM的编译时间。结合此数据可对codegen_cost_threshold进一步调整以获取更好的查询性能。5. GaussDB LLVM性能表现GaussDB实验室分别就codegen打开和关闭进行了TPCH性能测试。▲ 表1 测试环境测试结果显示打开codegen时带有qual的SQL查询性能都有明显提升且提升比例与qual在整个SQL中的占比相关像Q6、Q12、Q19等qual占比较高的查询性能提升也较多。▲ 表2 TPCH 部分Query的测试结果TPCC的性能提升并没有TPCH那么多但据实验室数据打开codegen后tpmC提升了约7%。总结LLVM被广泛运用于数据库、大数据、AI等领域。在数据库领域多家商业数据库和开源数据库都应用其加速数据库处理。GaussDB作为企业级的数据库经过了多年的技术发展具备丰富的技术特性使用该技术后提升了系统的查询性能使得客户在OLAP和OLTP多场景中均受益。转载华为云论坛
【GaussDB】LLVM技术在GaussDB等数据库中的应用
万物互联的态势下数据量的激增使得“如何提升数据处理性能”成为各家数据库共同面临的挑战。作为编译优化技术的代表基于LLVM的CodeGen技术能为每个查询生成定制的机器码替代原本的通用函数减少实际查询时冗余的条件逻辑判断、虚函数调用并提高数据局域性从而达到提升查询整体性能的目的成为数据库性能优化的一项重要技术。LLVM能在分析类场景中给用户带来较大的收益也能在特定的交易性场景中给用户带来一定的收益。接下来详细解读一下LLVM技术在GaussDB中的应用吧。LLVM和数据库LLVMLow Level Virtual Machine是一款流行的开源编译器框架是CodeGen生成源代码的工具技术的事实标准被广泛运用于数据库如KESAnalyticDBGaussDB、大数据如Spark、AI平台如tensorflow等领域用于提升数据处理的性能。在没有引入LLVM这类CodeGen技术之前数据库会使用通用的处理逻辑来处理数据但通用逻辑“笨重”递归、封装、类型判断转换的代码实现方式存在虚函数开销、缓存使用率低下、对指令集不敏感等性能短板。引入LLVM之后可以为具体的查询生成定制化的机器码并尽可能的将数据存储在CPU的寄存器中进一步加快计算的速度LLVM天然支持JIT该技术可以解决条件逻辑冗余的问题减少大量的虚函数调用将数据尽可能地从内存加载到Cache上LLVM做了很多自动矢量化的工作比如下图左侧是通用代码右侧是CodeGen之后的代码。CodeGen根据实际情况消除了不必要的循环和判断。▲ 图1 通用性处理逻辑和LLVM代码示意另外LLVM技术可以有不同的实现粒度。比如可使用LLVM加速表达式计算或再进一步将多个算子融合编译成定制的机器码或将自定义函数、存储过程等编译成定制的机器码。▲ 图2 LLVM的实现粒度数据库在执行引擎中运用LLVM技术提升SQL的执行速度。如下图所示▲ 图3 LLVM技术运用于执行引擎LLVM适用场景LLVM对所有类型的SQL都会有收益吗答案是否定的。因为执行实时编译本身需要耗费一定的时间简单表达式能做到毫秒级复杂情况在百毫秒级对于查询本身耗时较少的场景加入LLVM反而会导致性能劣化。因此目前LLVM在OLAP/HTAP分析型业务场景中收益较大有着广泛应用而在OLTP交易型业务场景中则相对没有那么广泛。LLVM在OLTP中就一定没有收益吗答案同样是否定的。找对场景一样有收益。比如根据ISPRAS 2017年发表的实验结果jit-compiling sql queries in postgresql using llvm可知pgbench测试下OLTP场景中简单的查询加上JITJust-in-time及时编译LLVM天然支持扩展没有带来性能的提升甚至将TPS事务数/秒从21.8降低到了7.8。但是在Prepared queryplan cached的情况下叠加 JIT技术之后将TPS从20.7提升到了43性能上有了两倍的提升。▲ 图4 简单查询、CodeGen流程、Plancache和“Plancache CodeGen”流程的性能对比GaussDB中的LLVM1. LLVM在华为应用于数据库的时间线华为云数据库在LLVM上的研究还是非常超前的。早在2015年华为就作为某流行开源数据库社区的全球开发者大会的赞助商在会上发表的动态编译Go Faster with Native Compilation演讲引起了很大的反响。当时社区领袖Josh Burkus在其博客里面用一节篇幅专门详细介绍了华为动态编译的议题。▲ 图5 2015年社区领袖Josh Burkus介绍华为的动态编译议题在2017年华为在面向OLAP场景的数据库内核中突破了LLVM动态编译技术并在多个运营商、金融证券的POC项目中帮助客户提升数据处理性能同时在软件开发过程中充分模块化、通用化接口设计将LLVM同年落地到面向OLTP的数据库设计中。目前GaussDB数据库对于LLVM也在不断的演进开发。2. GaussDB LLVM实现简析GaussDB针对向量化引擎主要用于分析场景、行存主要用于交易场景都实现了CodeGen。如下图所示从代码模块层次来看1) GaussDB通过API接口层封装处理了LLVM环境、资源、基本元素等。2) GaussDB在CodeGen层调用API接口进行了不同粒度的实现。3) GaussDB在执行引擎侧根据情况使用CodeGen技术进行性能优化。▲ 图6 GaussDB LLVM 模块层次图GaussDB启动后会进行LLVM的初始化工作检查CPU对CodeGen的支持情况并进行环境初始化。在执行启动阶段以表达式为例程序会判断当前表达式是否可JIT是的话则会进行IR函数的生成和生成定制机器码及原本表达式执行函数的入口替代工作。在实际执行过程中运行处理函数该函数已经在上一阶段进行了入口替代进行实际执行工作。在执行结束后的清理阶段释放LLVM相关资源。▲ 图7 GaussDB CodeGen编译执行流程简图GaussDB使用了阈值codegen_cost_threshold来估算当前查询使用LLVM技术是否能带来收益。如果处理数据的规模大于该阈值后才会继续使用LLVM技术进行相关处理。该阈值代表行数也可以理解成处理数据的规模默认值为100000行可以调节。在OLAP场景中GaussDB在判断是否能够对于一个算子进行CodeGen后如数据类型算子类型判断等开始生成对应的IR bytecode片段之后MCJIT模块会调用生成的LLVM Module单元进行执行。在OLTP场景中GaussDB则会在Plan Cache场景下结合CodeGen框架通过缓存机器码的方式节省下编译生成中间语言IR Func以及优化成机器码的时间整个过程是异步的。因此在大量重复查询的场景下后续的查询也会因为LLVM技术而受益。另外为了避免行数估计错误而选择CodeGen导致性能劣化GaussDB还研发了当前业界独有的异步编译功能即在查询语句确定要使用CodeGen的时候将编译工作转交给后台线程工作线程在JIT函数编译完成前继续使用原始执行逻辑执行编译完成后再替换成JIT函数执行。3. GaussDB LLVM支持加速的场景支持LLVM的表达式行存表达式计算支持的数据类型不受限制。在向量化执行引擎中仅当表达式出现在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动态编译优化。在行执行引擎中除一次性的表达式计算外会考虑为所有算子的filter和Targetlist表达式都使用LLVM动态编译优化。支持LLVM的算子Join HashJoin仅向量化执行引擎支持Agg HashAggSort仅向量化执行引擎支持其中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工具进行显示。4. GaussDB LLVM使用建议GUC参数enable_codegen控制LLVM特性的打开和关闭。目前数据库内核侧默认打开。codegen_cost_threshold使用处理行数控制是否开启codegen默认为10000。10000是通过实验验证得出的优化值不建议将此值设置的过低。另外在开启LLVM特性的前提下建议在允许的条件下尽可能设置较大的work_mem如果出现大量下盘则建议关闭LLVM动态编译优化。用户可通过analysis_options为on(LLVM_COMPILE)执行对应查询语句在User Define Profiling中就可以看到LLVM的编译时间。结合此数据可对codegen_cost_threshold进一步调整以获取更好的查询性能。5. GaussDB LLVM性能表现GaussDB实验室分别就codegen打开和关闭进行了TPCH性能测试。▲ 表1 测试环境测试结果显示打开codegen时带有qual的SQL查询性能都有明显提升且提升比例与qual在整个SQL中的占比相关像Q6、Q12、Q19等qual占比较高的查询性能提升也较多。▲ 表2 TPCH 部分Query的测试结果TPCC的性能提升并没有TPCH那么多但据实验室数据打开codegen后tpmC提升了约7%。总结LLVM被广泛运用于数据库、大数据、AI等领域。在数据库领域多家商业数据库和开源数据库都应用其加速数据库处理。GaussDB作为企业级的数据库经过了多年的技术发展具备丰富的技术特性使用该技术后提升了系统的查询性能使得客户在OLAP和OLTP多场景中均受益。转载华为云论坛