GCC,MSVC,LLVM以及可能的其他工具链支持链接时(整个程序)优化,以允许在编译单元之间优化调用。
在编译生产软件时是否有理由不启用此选项?
答案 0 :(得分:26)
我认为“生产软件”是指您发送给客户/投入生产的软件。 Why not always use compiler optimization?的答案(由Mankarse友情指出)主要适用于您想要调试代码的情况(因此软件仍处于开发阶段 - 而非生产中)。
我能想到的唯一好的,有效的原因是链接时间优化可能会引入微妙的错误,请参阅Link-time optimization for the kernel。假设您有适当的测试来检查您即将发布的软件的正确性,我认为没有理由不默认使用LTO。 (随着时间的推移,LTO越来越成熟,所以我们希望这些微妙的错误会越来越少。)
答案 1 :(得分:8)
This recent question提出了另一种可能(但相当具体)LTO可能产生不良影响的情况:如果有问题的代码用于计时,并且已使用单独的编译单元来保持相对顺序在仪表和仪表声明中,LTO很有可能破坏必要的排序。
我确实说过具体。
答案 2 :(得分:3)
如果您编写的代码很好,那应该只是有利的。您可能会遇到编译器/链接器错误,但这适用于所有类型的优化,这很少见。
最大的缺点是它会大大增加链接时间。
答案 3 :(得分:0)
除了this,
考虑嵌入式系统的典型示例,
void function1(void) { /*Do something*/} //located at address 0x1000
void function2(void) { /*Do something*/} //located at address 0x1100
void function3(void) { /*Do something*/} //located at address 0x1200
通过预定义的寻址功能,可以通过相对的地址(如波纹管)调用
void (*ptr)(void) = function1;
(ptr + 0x100)(); //expected to call function2
(ptr + 0x200)(); //expected to call function3
很多都会导致意外行为。
答案 4 :(得分:0)
LTO 支持存在问题,并且 LTO 相关问题对编译器开发人员来说优先级最低。例如:mingw-w64-x86_64-gcc-10.2.0-5
适用于 lto,mingw-w64-x86_64-gcc-10.2.0-6
带有虚假地址的段错误。我们刚刚注意到 windows CI 停止工作。
请以 following issue 为例。
答案 5 :(得分:0)
该标准没有强制要求所有实现都支持完成所有任务所必需的语义,而是允许旨在适合各种任务的实现通过在超出 C 标准要求的极端情况下定义语义来扩展语言,方式是对这些任务很有用。
这种形式的一个非常流行的扩展是指定以与平台的应用程序二进制接口一致的方式处理跨模块函数调用,而不考虑 C 标准是否需要这种处理。
因此,如果对一个函数进行跨模块调用,例如:
uint32_t read_uint32_bits(void *p)
{
return *(uint32_t*)p;
}
生成的代码将读取地址为 p
的 32 位存储块中的位模式,并使用平台的本机 32 位整数格式将其解释为 uint32_t
值,而不考虑了解该存储块如何保存该位模式。同样,如果编译器被赋予类似的东西:
uint32_t read_uint32_bits(void *p);
uint32_t f1bits, f2bits;
void test(void)
{
float f;
f = 1.0f;
f1bits = read_uint32_bits(&f);
f = 2.0f;
f2bits = read_uint32_bits(&f);
}
编译器将在堆栈上为 f
保留存储空间,将 1.0f 的位模式存储到该存储中,调用 read_uint32_bits
并存储返回值,将 2.0f 的位模式存储到该存储中存储,调用 read_uint32_bits
并存储返回的值。
标准没有提供语法来表明被调用的函数可能会使用类型 uint32_t
读取它接收到的地址的存储,也没有表明给定函数的指针可能是使用类型 {{1} 写入的},因为用于低级编程的实现已经将语言扩展到支持此类语义,而无需使用特殊语法。
不幸的是,添加链接时间优化会破坏任何依赖于该流行扩展的代码。有些人可能会认为这样的代码被破坏了,但是如果人们认识到 C 原则的精神“不要阻止程序员做需要做的事情”,那么标准未能强制要求支持流行的扩展就不能被视为有意弃用如果标准未能提供任何合理的替代方案,则其使用。
答案 6 :(得分:-1)
链接时间优化可能导致错误代码的意外行为的一种情况如下:
想象一下,您有两个源文件read.c
和client.c
,它们被编译成单独的目标文件。在文件read.c
中,有一个函数read
除了读取特定的内存地址外,不执行其他任何操作。但是,此地址上的内容应标记为volatile
,但是很遗憾,该内容已被忘记。从client.c
中,函数read
被同一函数多次调用。由于read
仅执行一次从地址的读取操作,并且没有超出read
函数范围的优化,因此read
将始终在被调用时访问相应的存储位置。因此,每次从read
调用client.c
时,client.c
中的代码都会从该地址中获得新读取的值,就像使用了volatile
一样。>
现在,通过链接时间优化,可以从read
调用的任何地方内联来自read.c
的微小函数client.c
。由于缺少volatile
,编译器现在将意识到该代码从同一地址读取了几次,因此可以优化内存访问。因此,代码的行为开始有所不同。