And*_*ock 16 c++ optimization linker
最近,一位同事向我指出,将所有内容编译为单个文件创建的代码比编译单独的目标文件更有效 - 即使启用了链接时优化.此外,该项目的总编译时间显着下降.鉴于使用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;
}
Run Code Online (Sandbox Code Playgroud)
int foo (int x) { return x++; }
Run Code Online (Sandbox Code Playgroud)
运行结果:使用链接foo而不是内联,性能达到1.8%foo2.
$ ./release/testlink.exe 100000000
time : 13.375
time : 13.14
Run Code Online (Sandbox Code Playgroud)
是的,链接器优化标志(/ LTCG)已打开.
Rem*_*anu 27
你的同事已经过时了.该技术自2003年以来(在MS C++编译器上)已经在这里:/ LTCG.链接时间代码生成正是处理这个问题.据我所知,GCC在下一代编译器的雷达上具有此功能.
LTCG不仅优化代码,如跨模块内联函数,还实际重新排列代码以优化缓存局部性和特定负载的分支,请参阅配置文件引导优化.这些选项通常仅为Release版本保留,因为构建可能需要数小时才能完成:将链接已检测的可执行文件,运行分析负载,然后再次与分析结果链接.该链接包含有关LTCG的优化内容的详细信息:
内联 - 例如,如果存在经常调用函数B的函数A,并且函数B相对较小,则配置文件引导的优化将在函数A中内联函数B.
虚拟呼叫推测 - 如果虚拟呼叫或通过功能指针的其他呼叫经常以某个功能为目标,则配置文件引导的优化可以向有针对性的功能插入有条件执行的直接呼叫,并且可以内联直接呼叫.
寄存器分配 - 使用配置文件数据进行优化可以实现更好的寄存器分配
基本块优化 - 基本块优化允许在给定帧内暂时执行的通常执行的基本块放置在同一组页面(局部性)中.这最大限度地减少了使用的页数,从而最大限度地减少了内存开
大小/速度优化 - 程序花费大量时间的功能可以针对速度进行优化.
功能布局 - 基于调用图和分析的调用者/被调用者行为,往往沿同一执行路径的函数放在同一部分中.
条件分支优化 - 使用值探测器,概要文件引导的优化可以查找switch语句中的给定值是否比其他值更频繁地使用.然后可以从switch语句中提取该值.使用if/else指令可以完成相同的操作,优化器可以对if/else进行排序,以便if或else块首先放置,具体取决于哪个块更频繁为真.
死代码分离 - 在分析期间未调用的代码将移动到附加到该组部分末尾的特殊部分.这有效地使这一部分远离常用页面.
EH代码分离 - 当特殊情况引导的优化可以确定异常仅在异常条件下发生时,异常执行的EH代码通常可以移动到单独的部分.
记忆内在 - 如果可以确定是否经常调用内在函数,则可以更好地确定内在函数的扩展.还可以基于移动或复制的块大小来优化内在函数.
我不是编译器专家,但我认为编译器在语言树上运行时有更多的信息可供优化,而不是链接器必须满足于对对象输出进行操作,表达能力要差得多比编译器看到的代码。因此,链接器和编译器开发团队花费更少的精力来进行链接器优化,理论上可以匹配编译器所做的技巧。
顺便说一句,很抱歉我把你原来的问题转移到了 ltcg 讨论中。我现在明白你的问题有点不同,更关心链接时间与编译时间静态优化可能/可用。