最近,一位同事向我指出,将所有内容编译成单个文件创建的代码比编译单独的目标文件更有效 - 即使启用了链接时间优化。此外,该项目的总编译时间显着下降。鉴于使用C ++的主要原因之一是代码效率,这对我来说是令人惊讶的。
显然,当归档器/链接器从目标文件中创建库或将它们链接到可执行文件时,即使是简单的优化也会受到惩罚。在下面的示例中,由链接器而不是编译器完成时,平凡内联的性能成本为1.8%。似乎编译器技术应该足够先进以处理像这样的相当常见的情况,但它没有发生。
以下是使用Visual Studio 2008的简单示例:
#include <cstdlib>
#include <iostream>
#include <boost/timer.hpp>
using namespace std;
int foo(int x);
int foo2(int x) { return x++; }
int main(int argc, char** argv)
{
boost::timer t;
t.restart();
for (int i=0; i<atoi(argv[1]); i++)
foo (i);
cout << "time : " << t.elapsed() << endl;
t.restart();
for (int i=0; i<atoi(argv[1]); i++)
foo2 (i);
cout << "time : " << t.elapsed() << endl;
}
int foo (int x) { return x++; }
运行结果:使用关联的foo
代替内联foo2
,性能达到1.8%。
$ ./release/testlink.exe 100000000
time : 13.375
time : 13.14
是的,链接器优化标志(/ LTCG)已打开。
答案 0 :(得分:27)
你的同事已经过时了。该技术自2003年以来一直在这里(在MS C ++编译器上):/LTCG。链接时间代码生成正是处理这个问题。据我所知,GCC在下一代编译器的雷达上具有此功能。
LTCG不仅优化代码,如跨模块内联函数,还实际重新排列代码以优化缓存局部性和特定负载的分支,请参阅Profile-Guided Optimizations。这些选项通常仅为Release版本保留,因为构建可能需要数小时才能完成:将链接已检测的可执行文件,运行分析负载,然后再次与分析结果链接。该链接包含有关LTCG的优化内容的详细信息:
内联 - 例如,如果有 经常存在一个函数A. 调用函数B,函数B是 相对较小,然后轮廓引导 优化将内联函数B. 在功能A中。
虚拟来电投机 - 如果是 虚拟呼叫,或通过其他呼叫 函数指针,经常以a为目标 某些功能,配置文件引导 优化可以插入一个 有条件执行的直接电话 经常有针对性的功能,和 直接调用可以内联。
注册分配 - 使用优化 个人资料数据效果更好 注册分配。
基本块优化 - 基本块 优化允许通常执行 暂时执行的基本块 在给定的框架内放置 同一组页面(地点)。这个 最小化使用的页数, 从而最大限度地减少内存开销。
尺寸/速度优化 - 功能 该计划花费了大量时间 可以针对速度进行优化。
功能布局 - 基于通话 图形和配置文件的调用者/被调用者 行为,往往是功能 沿着相同的执行路径 放在同一部分。
条件分支优化 - 有 值探针,轮廓引导 优化可以找到给定的 使用switch语句中的值 比其他价值更频繁。这个 然后可以将价值拉出 切换声明。同样可以做到 用if / else指令在哪里 优化器可以命令if / else 那个if或else块是 首先放置取决于哪个块 更常见的是。
死代码分离 - 代码是 在分析期间未调用被移动 到附加的特殊部分 到一组部分的末尾。 这有效地保留了这一部分 经常使用的页面。
EH代码分离 - EH代码, 特别执行,可以 经常被移到一个单独的部分 当配置文件引导的优化可以 确定发生异常 只有在特殊条件下。
记忆内在性 - 扩展 如果它可以更好地决定内在函数 可以确定内在是否 经常打电话。内在的能 也可以根据块进行优化 移动或复制的大小。
答案 1 :(得分:3)
我不是编译专家,但我认为编译器有更多的信息可供处理以优化,因为它在语言树上运行,而不是必须满足自己在对象输出上操作的链接器,远不如编译器看到的代码那么富有表现力。因此,链接器和编译器开发团队花费更少的精力来进行链接器优化,理论上可以匹配编译器所做的技巧。
顺便说一句,对不起,我把你原来的问题分散到了ltcg的讨论中。我现在明白你的问题有点不同,更关心链接时间与编译时可能/可用的静态优化。答案 2 :(得分:2)
你的同事比我们大多数人聪明。即使它最初看起来是粗略的方法,项目内联到单个.cpp文件有一件事,其他方法,如链接时间优化没有,也不会有 - 可靠性 < / p>
然而,两年前你问过这个问题,我证明自那以后发生了很多变化(至少使用g ++)。例如,虚拟化更加可靠。