通过生成 llvm IR,提高程序的执行性能

来源:互联网 发布:php运行环境配置 编辑:程序博客网 时间:2024/05/17 05:05

LLVM IR 简介

按照 LLVM 官网 的解释:

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines, though it does provide helpful libraries that can be used to build them. The name “LLVM” itself is not an acronym; it is the full name of the project.

发展到现在 LLVM 已经不仅仅是一个编译器项目了,不过为了介绍 IR,我们不妨从编译器的角度来介绍一下 LLVM。编译器分为前端和后端,前端主要包括语法解析、语义分析以及生成抽象语法树,而后端则包括了汇编代码、目标文件的生成以及连接等等过程。LLVM 的目标是使前端和后端高度模块化,高度的每个模块都可重用,具体的做法就是通过 LLVM IR 连接前端和后端

LLVM IR 可以抽象的理解为 java 的字节码,后端理解成 jvm 的虚拟机,这样编译器开发人员就可以很方便的为某种语言开发一套编译器了:他只需要实现一个前端,用 LLVM IR 表示抽象语法树即可。而要添加对某个处理器平台的支持只需要添加对应平台的后端支持即可

为什么使用 LLVM IR

让我们来看一段伪代码:

for (int i = 0; i < num_fields_; ++ i) {    switch (type_[i]) {        case INTEGER:            ProcessIntegerField(field_[i]);            break;        case STRING:            ProcessStringField(field_[i]);            break;        ......    }}

类似这样的代码我们经常遇见,它有两个性能热点:1. for 循环的条件判断:i < num_field_s;2. 循环体中的 switch case 表达式

而在运行时,num_fields_ 以及 type_[i] 的值都是确定的,更准确的说:在执行上面那段代码钱,这两个值都是确定的,因此相比于上面的代码,下面的代码更加高效:

ProcessIntegerField(field_[0]);ProcessStringField(field_[1]);ProcessStringField(field_[2]);ProcessDoubleField(field_[3]);

上面的代码中,我们假设 num_fields_ 的值为 4,type_[0]type_[3] 的值分别为 INTEGER、STRING、STRING 以及 DOUBLE。这样的代码没有了分支判断,极大的增加了程序的执行性能

上面的例子告诉我们,在程序运行时,根据那些在函数运行前能确定的值,我们可以通过生成一个更加高效的、实现同样功能的函数来提高程序的执行效率,尤其是当那个函数被反复调用多次的时候,优化效果尤为明显

因为生成函数需要时间,实际上我们不一定非要选择 IR,完全可以选择其他语言,比如 C/Java/Python/PHP 等等。从生成函数到函数被编译成机器码也是一个时间开销,这个时间越短越好,而生成 IR 再利用 llvm 的 JIT 技术,这一时间被控制的非常非常短(随 IR 的大小而变化),因此 LLVM IR 成了不二的选择:利用 LLVM 提供的函数库在程序运行时根据已知的变量信息动态生成函数的 LLVM IR 代码,接下来利用 LLVM 的 JIT 库把 IR 代码转化为对应的机器码,并返回函数的指针,最后程序拿到函数指针调用生成的函数进行计算

如何编写 IR

先准备 LLVM 环境,你需要安装 llvm 和 clang,这里不对安装环境做过多描述

有两种方法编写 IR:一种是通过 LLVM 提供的 IRBuilder 来一条指令一条指令的生成 IR 代码;另一种是先用 C/C++ 写出需要生成 IR 代码的函数,然后用 clang 把 C/C++ 代码转化为 IR 代码

0 0
原创粉丝点击