自编教材实操课程分享:第五章—编译指示
先进编译实验室
2024年05月04日 08:00
收录于文集
共28篇


本文主要介绍编译指示。实验环境是CentOS7 + LLVM。

1. 优化方法简述

编译指示是设定编译器的状态或是指示编译器完成一些特定的的动作。LLVM提供了一种编译指示语句用于有选择地开启或关闭优化,语法如下:

#pragma clang optimize on

.......

#pragma clang optimize off

在on和off之间的所有函数定义都会被优化,该范围之外的函数则不进行优化。

这里我们重点讲解关于循环的编译指示优化,#pragma clang loop是针对编译优化的编译指示语句,用于优化指令的for、while、do-while循环体。优化人员可以通过编译指示开启优化功能,但还需要通过LLVM编译器的优化合法性分析,如果未通过合法性分析则不进行优化。

1.1 向量化

优化人员可以通过指定特定的循环进行向量化,其编译指示语句如下:

(1)#pragma clang loop vectorize(enable):开启自动向量化

(2)#pragma clang loop interleave(enable):开启基本块内展开

(3)#pragma clang loop vectorize_width(value) interleave_count(value):指定向量化宽度和基本块内展开因子

如果添加的编译指示语句指定的优化参数未通过LLVM编译器的合法性分析,则不进行优化。

1.2 循环展开

循环展开的编译指示语句如下:

(1)#pragma clang loop unroll(enable):开启循环展开优化,编译器会通过自身的优化算法计算展开因子并实现循环展开。

(2)#pragma clang loop unroll(full):在迭代次数N已知的情况下执行完全循环展开,否则不执行完全循环展开。

(3)#pragma clang loop unroll_count(value):指定展开因子,其中value只能为正整数,如果此值大于循环迭代次数,则循环将按照循环迭代次数进行完全展开,否则将按照指定的展开因子进行优化。

1.3 循环分布

#pragma clang loop distribute(enable)编译指示语句可以指定某一个循环开启循环分布优化。如果编译指示语句指定distribute(enable),并且循环中存在因依赖关系而导致循环不能向量化的语句,编译器通过合法性分析后,会将有依赖关系的循环分布到一个新循环中。

2. 示例分析

2.1 示例一

示例一是一个循环向量化的例子,功能是实现数组赋值以及求和操作。可以在第二个for循环之前通过编译指示语句开启向量化,这里我们指示了它的向量化宽度为4,基本块内展开因子为8。代码如下所示,优化前的代码命名为pragma_loop_vectorize-2-before.c,优化后的代码命名为pragma_loop_vectorize-2-after.c。

运行命令:

(1)clang pragma_loop_vectorize-2-before.c -o pragma_loop_vectorize-2-before

(2)./pragma_loop_vectorize-2-before

(3)clang -O1 pragma_loop_vectorize-2-after.c -Rpass=vectorize -o pragma_loop_vectorize-2- after

(4)./pragma_loop_vectorize-2-after

可以看出,优化前后,得到的计算结果都是一样的,但是经过编译指示语句的向量化优化后,程序的执行时间明显要比优化前的执行时间短,达到了优化的效果。

2.2 示例二

示例二是一个通过编译指示语句开启循环展开优化的例子。通过编译指示语句#pragma clang loop unroll(enable) 开启循环展开优化,编译器会通过自身的优化算法计算展开因子并实现循环展开。优化前的代码命名为pragma_loop_unroll-before.c,优化后的代码命名为pragma_loop_unroll-after.c。

运行命令:

(1)clang pragma_loop_unroll-before.c -o pragma_loop_unroll-before

(2)./pragma_loop_unroll-before

(3)clang -O1 pragma_loop_vectorize-2-after.c -Rpass=vectorize -o pragma_loop_vectorize-2- after

(4)./pragma_loop_unroll-after

可以看出,优化前后,得到的计算结果都是一样的,但是经过编译指示语句开启循环展开优化后,程序的执行时间明显要比优化前的执行时间短,达到了优化的效果。

之后,通过如下命令可以生成优化前后的中间代码:

(5)clang pragma_loop_unroll-before.c -S –emit-llvm –o pragma_loop_unroll-before.ll

(6)clang -O1 pragma_loop_unroll-after.c -S –emit-llvm -o pragma_loop_unroll-after.ll

2.3 示例三

示例三是一个通过编译指示语句开启循环分布优化的例子。编译器内的循环分布优化遍会分析每一个循环并实现优化,如果优化人员想指定某一个循环实现循环分布,则可以通过编译指示语句指定循环#pragma clang loop distribute(enable)开启循环分布优化。优化前的代码命名为:pragma_loop_distribute-before.c,优化前的代码命名为:pragma_loop_distribute-after.c。

运行命令:

(1)clang pragma_loop_distribute-before.c -o pragma_loop_distribute-before

(2)./pragma_loop_distribute-before

(3)clang -O1 pragma_loop_distribute-after.c -w -Rpass=loop-distribute -Rpass-missed=loop-d istribute -o pragma_loop_distribute-after

(4)./pragma_loop_distribute-after

可以看出,优化前后,得到的计算结果都是一样的,但是经过编译指示语句开启循环分布优化后,程序的执行时间明显要比优化前的执行时间短,达到了优化的效果。

之后,通过如下命令可以生成优化前后的中间代码:

(5)clang pragma_loop_distribute-before.c -S -emit-llvm -o pragma_loop_distribute-before.ll

(6)clang -O1 pragma_loop_distribute-after.c -S -emit-llvm -w -o pragma_loop_distribute-after.ll

可以看出,优化后的中间代码明显要比优化前的中间代码简洁。

3. 总结

编译指示是设定编译器的状态或是指示编译器完成一些特定的的动作。为了便于开启和关闭优化功能,LLVM提供了一种编译指示语句用于有选择地开启或关闭优化,其语法为#pragma clang optimize 后跟on或者off,在on和off之间区域的所有函数定义都会被优化。

4. 参考资料

(1)编译指示(Pragma Directives)-CSDN博客

(2)编译器优化那些事儿(12):LLVM 自动向量化-CSDN博客