根据 GCC 手册,-fipa-pta
优化会:
-fipa-pta
:执行过程间指针分析和过程间修改和引用分析。此选项可能会导致在大型编译单元上使用过多的内存和编译时。在任何优化级别默认情况下都不会启用它。
我假设 GCC 试图根据过程中使用的指针和引用来区分可变和不可变数据。有更深入的 GCC 知识的人可以解释什么-fipa-pta
吗?
根据 GCC 手册,-fipa-pta
优化会:
-fipa-pta
:执行过程间指针分析和过程间修改和引用分析。此选项可能会导致在大型编译单元上使用过多的内存和编译时。在任何优化级别默认情况下都不会启用它。
我假设 GCC 试图根据过程中使用的指针和引用来区分可变和不可变数据。有更深入的 GCC 知识的人可以解释什么-fipa-pta
吗?
我认为“程序间”这个词是这里的关键。
我对 gcc 的优化器不是很熟悉,但我之前一直致力于优化编译器。以下内容有些推测;与一小粒盐一起服用,或与了解 gcc 内部结构的人确认。
优化编译器通常仅在每个单独的函数(或子例程或过程,取决于语言)内执行分析和优化。例如,给定这样的人为示例的代码:
double *ptr = ...;
void foo(void) {
...
*ptr = 123.456;
some_other_function();
printf("*ptr = %f\n", *ptr);
}
优化器将无法确定 的值是否*ptr
已被调用更改some_other_function()
。
如果启用了过程间分析,那么优化器可以分析 的行为some_other_function()
,并且它可能能够证明它不能修改*ptr
。鉴于这样的分析,它可以确定表达式*ptr
仍然必须计算为123.456
,并且原则上它甚至可以用 替换printf
调用puts("ptr = 123.456");
。
(事实上,使用与上述代码片段类似的小程序,我得到了与-O3
and相同的生成代码-O3 -fipa-pta
,所以我可能遗漏了一些东西。)
由于一个典型的程序包含大量的函数,以及大量可能的调用序列,这种分析可能非常昂贵。
正如这篇文章所引用的:
“-fipa-pta”优化在分析时会考虑被调用函数的主体,因此编译
void __attribute__((noinline))
bar(int *x, int *y)
{
*x = *y;
}
int foo(void)
{
int a, b = 5;
bar(&a, &b);
return b + 10;
}
with -fipa-pta 使编译器看到 bar 没有修改 b,编译器通过将 b+10 更改为 15 来优化 foo
int foo(void)
{
int a, b = 5;
bar(&a, &b);
return 15;
}
一个更相关的例子是“<a href="https://kristerw.blogspot.se/2017/05/integer-division-is-slow.html" rel="nofollow noreferrer">Integer 中的“慢”代码分工很慢”博客文章
std::random_device entropySource;
std::mt19937 randGenerator(entropySource());
std::uniform_int_distribution<int> theIntDist(0, 99);
for (int i = 0; i < 1000000000; i++) {
volatile auto r = theIntDist(randGenerator);
}
用 -fipa-pta 编译它可以让编译器看到 intDist 在循环中没有被修改,因此内联代码可以像“快速”版本一样被常量折叠——结果它的运行速度快了四倍.