背景
编译器会为代码做编译优化
一般来说,编译器的工作会做得很好
但是由于C++过于灵活,导致编译器不敢过度优化
opt-viewer是llvm提供的一款显示编译优化的工具
它可以清晰的标记出clang为c++做了什么优化,没有做哪些优化,没有优化的原因是什么
通过阅读opt-viewer的报告,可以微调代码,让编译器编译出更优的代码
opt-viewer用法
clang++ -O3 -fsave-optimization-record -c foo.c
opt-viewer.py foo.opt.yaml html -j 1
用例分析
Clobbered by store
代码片段:
void foo(int* p, const int& b) {
for (int i = 0; i < 10; i++) {
p[i] += b;
}
}
opt-viewer报告:
opt-viewer-store报告分析:
看到绿色的为编译优化成功
红色为编译优化失败
看倒数第二行出现了Clobbered by store
代表变量b,在每次循环中都要加载b的内容
因为b有可能是p的引用
在循环中b的值有可能发生变化
优化代码
void foo(int* __restrict__ p, const int& b) {
for (int i = 0; i < 10; i++) {
p[i] += b;
}
}
再次查看报告
opt-viewer-fix-storeClobbered by store 问题解决了
Clobbered by call
代码片段
void somefunc(const int&);
int whateva();
void f(int i, int* res) {
somefunc(i);
i++;
res[0] = whateva();
i++;
res[1] = whateva();
i++;
res[2] = whateva();
}
opt-viewer报告
opt-viewer-call报告分析
变量 i 必须在每次调用 whateva 函数前进行累加
不能优化成 i += 3;
因为 somefunc 获取了 i 的引用
而 whateva 有可能对 i 的引用做任何事情
这就出现了 Clobbered by call
优化代码
使用_attribute_((pure))
void somefunc(const int&) __attribute__((pure));
int whateva();
void f(int i, int* res) {
somefunc(i);
i++;
res[0] = whateva();
i++;
res[1] = whateva();
i++;
res[2] = whateva();
}
报告
opt-viewer-fix-call1分析
_attribute_((pure))函数代表somefunc的作用只会影响返回值,不会影响其他任何变量
因此somefunc不会对变量 i 发生副作用
优化代码
使用_attribute_((const))
void somefunc(const int&);
int whateva() __attribute__((const));
void f(int i, int* res) {
somefunc(i);
i++;
res[0] = whateva();
i++;
res[1] = whateva();
i++;
res[2] = whateva();
}
查看报告
opt-viewer-fix-call2报告分析
看到出现了 tailcallelim
函数的尾四行都没有了
代表 i++ 被合并了, whateva 也只调用了一次
这是因为_attribute_((const))修饰的函数,是常函数,它的输出只依赖函数的输入,不依赖任何全局变量
Failed to move load with loop invariant address
代码片段
class C {
bool m_cond;
void method1();
};
void f();
void g();
void C::method1() {
for (int i = 0; i < 5; ++i) {
g();
if (m_cond) f();
}
}
opt-viewer报告
opt-viewer-loop报告分析
for循环不能把条件m_cond往外提
因为变量m_cond有可能在循环的时候被其他函数修改
优化代码
class C {
bool m_cond;
void method1();
};
void f();
void g();
void C::method1() {
bool cond = m_cond;
for (int i = 0; i < 5; ++i) {
g();
if (cond) f();
}
}
查看报告
opt-viewer-loop-fix报告分析
使用临时变量代替引用变量
可以更好的做循环优化
网友评论