考虑以下方案。我们有 3 个文件:
main.cpp:
int main() {
clock_t begin = clock();
int a = 0;
for (int i = 0; i < 1000000000; ++i) {
a += i;
}
clock_t end = clock();
printf("Number: %d, Elapsed time: %f\n",
a, double(end - begin) / CLOCKS_PER_SEC);
begin = clock();
C b(0);
for (int i = 0; i < 1000000000; ++i) {
b += C(i);
}
end = clock();
printf("Number: %d, Elapsed time: %f\n",
a, double(end - begin) / CLOCKS_PER_SEC);
return 0;
}
class.h:
#include <iostream>
struct C {
public:
int m_number;
C(int number);
void operator+=(const C & rhs);
};
class.cpp
C::C(int number)
: m_number(number)
{
}
void
C::operator+=(const C & rhs) {
m_number += rhs.m_number;
}
使用带有 -std=c++11 -O3
标志的 clang++ 编译文件。
我所期望的性能结果非常相似,因为我认为编译器会优化运算符,使其不被称为函数。现实虽然有点不同,但结果如下:
Number: -1243309312, Elapsed time: 0.000003
Number: -1243309312, Elapsed time: 5.375751
我玩了一会儿发现,如果我将 class.* 中的所有代码粘贴到 main.cpp 中,速度会显着提高并且结果非常相似。
Number: -1243309312, Elapsed time: 0.000003
Number: -1243309312, Elapsed time: 0.000003
我意识到这种行为可能是由于 main.cpp 和 class.cpp 的编译是完全分开的,因此编译器无法执行足够的优化。
我的问题:是否有任何方法可以保持 3 文件方案并仍然达到优化级别,就好像文件被合并为一个而不是编译一样?我读过一些关于“统一构建”的文章,但这似乎有点过头了。
最佳答案
简答
您想要的是链接时间优化。试试 this question 的答案.即,尝试:
clang++ -O4 -emit-llvm main.cpp -c -o main.bc
clang++ -O4 -emit-llvm class.cpp -c -o class.bc
llvm-link main.bc class.bc -o all.bc
opt -std-compile-opts -std-link-opts -O3 all.bc -o optimized.bc
clang++ optimized.bc -o yourExecutable
您应该看到您的性能达到了将所有内容粘贴到 main.cpp
时的性能。
长答案
问题是编译器无法在链接期间内联重载运算符,因为它不再具有可用于内联它的形式的定义(它不能内联裸机代码)。因此,main.cpp
中的操作符调用将保持对class.cpp
中声明的函数的真正函数调用。与可以进一步优化(例如矢量化)的简单内联加法相比,函数调用非常昂贵。
当您启用链接时优化时,编译器能够做到这一点。正如您在上面看到的,您首先创建 llvm 中间表示字节码(.bc
文件,以下我将简称为 llvm 代码)而不是机器码。
然后将这些文件链接到一个新的 .bc
文件,该文件仍然包含 llvm 代码而不是机器代码。与机器代码相比,编译器能够对 llvm 代码执行内联。 opt
是 llvm 优化器(一定要安装 llvm
),它执行内联和进一步的链接时间优化。然后,我们最后一次调用 clang++
以从优化的 llvm 代码生成可执行的机器代码。
对于 GCC 患者
上面的答案仅适用于clang。 GCC (g++) 用户必须在编译和链接期间使用 -flto
标志以启用链接时间优化。它比使用 clang 更简单,只需在任何地方添加 -flto
:
g++ -c -O2 -flto main.cpp
g++ -c -O2 -flto class.cpp
g++ -o myprog -flto -O2 main.o class.o
关于C++ 运算符重载性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24930690/