同一LLVM Bitcode经clang -c与llc生成的目标文件性能差异原因探究
同一LLVM Bitcode经clang -c与llc生成的目标文件性能差异原因
问题重现
测试步骤如下:
# 1. 生成LLVM Bitcode clang demo.c -c -O3 -emit-llvm -o demo.c.bc # 分别通过llc和clang生成目标文件(注:需链接为可执行文件才能运行) llc -O3 demo.c.bc -filetype=obj -o demo_from_llc clang demo.c.bc -c -O3 -o demo_from_clang
执行测试(已链接为可执行文件):
time ./demo_from_llc_exec 500000000 # real 0m0.452s time ./demo_from_clang_exec 500000000 # real 0m0.545s
使用clang版本:10.0.0
核心原因分析
两者性能差异的本质是clang与llc处理Bitcode时的优化流程、默认参数存在区别,具体可拆解为以下几点:
1. 优化Pass组合的差异
虽然都指定了-O3,但clang在处理Bitcode文件时,并不会完全复用llc -O3的Pass序列:
- 直接调用
llc -O3会启用LLVM后端完整的高性能优化Pass,包括更激进的循环展开、指令调度、寄存器分配优化等。 - 而
clang -c编译Bitcode时,默认会跳过部分前端优化Pass(因为Bitcode已是前端处理后的产物),但同时未显式启用llc-O3中的全部后端优化,导致最终目标代码的优化程度不足。
2. 目标特定优化的启用差异
llc作为LLVM的后端代码生成器,-O3模式下会针对目标CPU启用更多硬件相关优化(比如特定指令集利用、缓存友好的代码布局等)。而clang在调用llc时,可能传递了默认参数限制了部分目标特定优化的发挥。
以测试代码为例,func函数循环调用lib_func,若llc成功将lib_func内联到循环中,并完成循环展开等优化,而clang生成的代码仍保留函数调用开销,就会出现明显性能差距。
3. Bitcode处理阶段的差异
clang编译Bitcode时会额外执行C/C++语义校验或转换步骤,这些步骤可能干扰后端优化效果;而llc是纯后端工具,直接对Bitcode进行代码生成,流程更简洁,优化更聚焦。
验证方法
要确认具体差异点,可通过以下方式排查:
- 生成汇编文件对比:分别执行
llc -O3 demo.c.bc -o demo_from_llc.s和clang demo.c.bc -c -O3 -S -o demo_from_clang.s,查看func函数的实现是否存在内联、循环优化等差异。 - 查看clang调用llc的参数:执行
clang demo.c.bc -c -O3 -v,输出clang调用llc的完整命令行参数,与直接执行的llc -O3参数对比,确认是否存在优化选项差异。
附录:测试代码
const int size = 10000; int val[10000] = {0}; int lib_func(int idx) { val[idx % size] += idx; return val[idx % size]; } int func(int num) { int ret = 0; for (int i = 0; i < num; ++i) { ret += lib_func(i); } return ret; } int get_num(const char *p) { int ret = 0; while (*p != '\0') { ret *= 10; ret += *p - '0'; p++; } return ret; } int main(int argc, char **argv) { int ret = func(get_num(argv[1])); return ret; }
内容的提问来源于stack exchange,提问作者zcfh
相关产品推荐
相关产品推荐

