代码生成 (Code Generation)

1 概述

Cloudera Impala是在Apache Hadoop上的开源实时执行引擎,并为了性能优化而做了很多工作。本文将解释如何利用运行时的代码生成技巧,提高CPU的使用率,减少整体执行时间,以及为何整体性能可以提升3倍。

2 为何使用代码生成

对于一个query,优化执行性能的最理想状态,就是创造一个应用,只支持这个query的数据格式,和查询类型。

举例来说,最理想的状态下,执行一下query的速度:

select count(*) from tbl where col like %XYZ%

grep -c "XYZ" tbl

是相同的。

考虑另一个query:select sum(col) from tbl。如果表只有一列,类型是int64,可以用一下代码执行:

int64_t sum=0;
int64_t* values = (int64_t*)buffer;
for (int i=0; i < num_rows; ++i) {
sum += values[i];
}

用以上两种方式执行query,通常比在通用的queryengine里执行要快的多(不考虑database使用索引等优化技术)。这主要是因为目前的queryengine存在以下的开销:

  • 虚函数调用:

在不使用代码生成技术的情况下,表达式的求值过程中通常需要调用虚函数,例如eval。这主要取决于系统的实现,通常来说,总会有一群operator类,每一个operator类都会实现eval函数。在这种情况下,表达式计算本身的开销很小,而虚函数调用带来的开销通常就会比较大。

  • 大量的switch case代码,需要对类型等进行判断:

尽管分支预测技术可以缓解这个问题,但是分支指令依然会降低执行pipeline的效率,以及影响指令级并行。

  • 无法通过使用常量提高性能:

在Impala中,每一个tuple的的长度在编译时就已经计算好。例如col3在tuple中的offset时16。如果把这些常量写入代码中,则可以减少额外的内存访问的开销。

代码生成的目标就是,对每一个query,都使用和定制程序几乎相同的指令数,并得到相同的结果。避免因为支持更多的功能而导致的额外开销。

3 llvm

LLVM(Low Level Virtual Machine)是一组库,包含了编译器的building blocks。主要的模块包括:

  • AST -> IR generation
  • IR优化
  • IR -> machine code generation

LLVM的IR和Java的byte code很类似。IR一种二进制语言,LLVM把IR当作其内部模块的输入和输出LLVM也提供更高级的code object(instruction object,function object),使得可以更方便的对IR进行编程。包括对函数进行inline,移除指令,用常量替代一个计算,等等。Impala用到了LLVM中的IR优化,以及IR生成机器码。

除了LLVM,还有其他的生成代码的方式,但LLVM的效果更好:

  • 直接生成可以执行的机器码:

尽管这个过程速度很快,但是生成机器码很容易出错,也很复杂,尤其是当函数数量增加时。同时,这种方法也无法享受编译器优化带来的性能提升。

  • 生成c++代码,编译,并动态加载:

这种方法生成的代码可以经过编译器优化,并且生成高级语言相对简单。但通常编译c++代码需要几秒的时间,是比较慢的。

4 Impala使用IR

在进行完语意分析后,impala为每一个独立的操作生成其kernal代码。在代码生成时,由于已经知道了数据的类型,数据的layout。所以生成的代码中,会对循环等进行很多的优化。

LLVM提供两种生成IR的机制。1)使用IrBuilder API;2)使用clang编译器。这两种方法Impala都用到了。

  1. 使用IrBuilder生成IR
  2. 加载已经编译好的IR,仅加载query需要的函数。
  3. 把1和2的结果combine到一起。
  4. 通过LLVM的优化器,对IR进行优化。
  5. JIT compile,把优化后的IR编译成机器码。LLVM会把它以函数指针的形势返回。

5 示例

select
l_returnflag,
l_linestatus,
sum(l_quantity),
sum(l_extendedprice),
sum(l_extendedprice * (1 - l_discount)),
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)),
avg(l_quantity),
avg(l_extendedprice),
avg(l_discount),
count(1)
from
tpch.lineitem
where
l_shipdate<='1998-09-02'
group by
l_returnflag,
l_linestatus

Impala会将这个query编译成一个算子树。对这个query来说,会有两个算子:scan算子,用于读取输入数据;聚合算子,计算sum和avg。

对于聚合算子,Impala会遍历所有每一条纪录,根据lreturnflags和llinestatus的值进行hash,在hash table中查找,并重新计算sum和avg的结果。生成后的代码会把对每一条纪录的所有求值操作放在一个被inline的循环中。

下图时执行结果对比:

对于两种不同的dataset,性能头提高了3倍。而代码生成消耗的时间大概为150s。代码生成消耗的时间和采用的优化选项有很大关系。可以在impala shell中,使用set命令查看优化选项。

下表是更详细的对比:

Codegen on? Time Instructions Branches Branch Misses Miss %
YES 0.63s 52,605,701,380 9,050,446,359 145,461,106 1.607
NO 1.7s 102,345,521,322 17,131,519,396 370,150,103 2.161

可以看到,采用代码生成可以减少一半的指令和一半的branch miss。

6 总结

我们在代码生生成上投入的精力已经得到了回报。随着我们继续的改进,我们期望得到更大的性能提升。列存储,更高效的编码方式,以及更大的缓存,这些技术会提高IO的性能。这时候,CPU的效率就将变得更加重要。

代码生成对于简单的query,带来的性能提升会更明显。对于query中带有复杂操作的,例如正则表达式,则性能提升会不那么显著。因为相对来说,正则表达本身的计算会占用更多时间。

在Impala 0.5中,还有一些代码没有使用代码生成技术。这些会在GA版本中得到改进。

Impala学习--代码生成(Code Generation)的更多相关文章

  1. Orchard Core 文档翻译 (二)代码生成模板 Code Generation Templates

    Code Generation Templates 翻译原文:https://www.cnblogs.com/Qbit/p/9746457.html转载请注明出处 Orchard Core Templ ...

  2. Impala学习–Impala后端代码分析

    Table of Contents 1 代码结构 2 StateStore 3 Scheduler 4 impalad启动流程 5 Coordinator 6 ExecNode 7 PlanFragm ...

  3. Object constraint language for code generation from activity models

    一.基本信息 标题:Object Constraint Language for Code Generation from Activity Models 时间:2018 出版源:Informatio ...

  4. 【Spark】Spark性能优化之Whole-stage code generation

    一.技术背景 Spark1.x版本中执行SQL语句,使用的是一种最经典,最流行的查询求职策略,该策略主要基于 Volcano Iterator Model(火山迭代模型).一个查询会包含多个Opera ...

  5. Code Generation and T4 Text Templates

    Code Generation and T4 Text Templates Code Generation and T4 Text Templates

  6. 如何在 PhpStorm 使用 Code Generation?

    實務上開發專案時,有一些程式碼會不斷的出現,這時可靠 PhpStorm 的 Code Generation 幫我們產生這些 code snippet,除此之外,我們也可以將自己的 code snipp ...

  7. Spark SQL includes a cost-based optimizer, columnar storage and code generation to make queries fast.

    https://spark.apache.org/sql/ Performance & Scalability Spark SQL includes a cost-based optimize ...

  8. impala学习笔记

    impala学习笔记 -- 建库 CREATE DATABASE IF NOT EXISTS database_name; -- 在HDFS文件系统中创建数据库,需要指定要创建数据库的位置. CREA ...

  9. 记一次antlr错误:ANTLR Tool version 4.5.3 used for code generation does not match the current runtime version 4.7.2ANTLR

    场景:重构spark 2.1版本的sql语法.因此 需要使用antlr: 前期准备:idea安装了antlr插件(antlr的4.7.2版本) 因此在maven工程中添加了antlr的依赖: < ...

  10. ASP.NET MVC 学习6、学习使用Code First Migrations功能,把Model的更新同步到DB中

     参考:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-new-field-to-th ...

随机推荐

  1. .net core 负载均衡取客户端真实IP

    一个网关代码(.net core 3.1),部署到负载均衡器有故障,发现获取到的客户端IP都是内网IP了,负载均衡用的是阿里云的SLB . 记录一下修改过程 在Strup.cs 中的 Configur ...

  2. Java核心知识体系9-并发与多线程:线程基础

    Java系列 Java核心知识体系1:泛型机制详解 Java核心知识体系2:注解机制详解 Java核心知识体系3:异常机制详解 Java核心知识体系4:AOP原理和切面应用 Java核心知识体系5:反 ...

  3. Coursera, Big Data 5, Graph Analytics for Big Data, Week 3

    Graph Analytics 有哪些类型 node type (labels) node schema: attributes 组成了schema. 同样的, Edge也有 Edge Type 和E ...

  4. MyBatisPlus——DML编程控制——乐观锁

    乐观锁 业务并发现象带来的问题:秒杀最后一单 用于中小型项目(2000请求以下) 添加一个数据库字段,使每次请求修改数据时,这个字段就加一,当有多人同时请求时,这些人同时获取到的都是相同的该字段,但当 ...

  5. YAML编写应用的资源清单文件(十五)

    上面我们在 Kubernetes 中部署了我们的第一个容器化应用,我们了解到要部署应用最重要的就是编写应用的资源清单文件.那么如何编写资源清单文件呢?日常使用的时候我们都是使用 YAML 文件来编写, ...

  6. WPF中的ListBox怎么添加删除按钮并删除所在行

    直接上代码: 第一步:创建测试类 public class BeautifulGirl { public string Name { get; set; } } 第二步:创建viewmodel和数据源 ...

  7. USB Type-C Power Role

    USB Power Role 是指 USB 设备在供电方面所扮演的角色,主要分为供电方(Provider)和受电方(Consumer).在 USB 供电协议中,电源角色的管理尤为重要,尤其是在 USB ...

  8. ftrace在应用上的使用

    之前介绍通过命令行配置和使用ftrace功能,但是实际中,我们也会希望抓C/C++程序中某段代码的调度情况.笔者前不久就遇到这种问题,某个函数调用时延概率超过100ms,是为什么?这时候就需要在他们代 ...

  9. CF753B题解

    这应该算是一个很脍炙人口的小游戏了吧 (没玩过算我没说) 因为一共有 \(50\) 次询问机会,那最简单直接的方法就不难想到. 我们把 \(0 \sim9\) 全部询问一遍,如果回答两个整数不全为 \ ...

  10. 运营商业务系统基于 KubeSphere 的容器化实践

    本篇文章是 KubeSphere 2020 年度 Meetup 上讲师宋磊分享内容整理而成. 大家好,我是宋磊,在运营商的一个科技子公司任职,主要做大数据业务.我主要负责公司的 IaaS 层和 Paa ...