在编写代码时,我经常会多次使用特定函数调用中的值。我意识到一个明显的优化就是在变量中捕获这些反复使用的值。这(伪码):
function add1(foo){ foo + 1; }
...
do_something(foo(1));
do_something_else(foo(1));变成:
function add1(foo){ foo + 1; }
...
bar = foo(1);
do_something(bar);
do_something_else(bar);然而,在我的经验中,这样做显式地降低了代码的可读性。我认为,如果我们选择的语言允许函数产生副作用,编译器就无法进行这种优化。
最近我研究了这个问题,如果我正确理解的话,这个优化就是/可以对函数必须是纯的语言进行优化。这并不令我感到惊讶,但据推测,这也可以用于不纯的功能。通过几个快速的Google搜索,我找到了以下代码片段:GCC 4.7改进
在执行前端优化时,即使对于不纯函数,也可以删除重复的函数调用。
编译器优化(维基百科)
例如,在某些语言中,函数不允许产生副作用。因此,如果一个程序使用相同的参数多次调用同一个函数,编译器可以立即推断该函数的结果只需要计算一次。在允许函数产生副作用的语言中,另一种策略是可能的。优化器可以确定哪个函数没有副作用,并将这种优化限制在无副作用的函数上。只有当优化器能够访问被调用的函数时,才有可能进行优化。
根据我的理解,这意味着优化器可以确定函数是纯的还是不纯的,并且在函数是纯函数时执行此优化。我这么说是因为,如果一个函数在给定相同的输入时总是产生相同的输出,并且没有副作用,那么它将满足两个条件,都被认为是纯的。
这两段话给我提了两个问题。
最后:
如果这些是愚蠢或不合逻辑的问题,我道歉。它们只是一些我最近一直很好奇的事情。:)
发布于 2012-09-20 08:59:48
免责声明:我不是一个编译器/优化者,我只喜欢偷看生成的代码,并且喜欢阅读那些东西--所以这不是自动的。一个快速的搜索并没有出现太多关于功能的消除,所以它可能会做一些额外的魔术,这里没有解释。
优化器可以
修改示例,并在C++中执行:
extern volatile int RW_A = 0; // see note below
int foo(int a) { return a * a; }
void bar(int x) { RW_A = x; }
int _tmain(int argc, _TCHAR* argv[])
{
bar(foo(2));
bar(foo(2));
}解析为(伪码)
<register> = 4;
RW_A = register;
RW_A = register;(注意:读取和写入易失性变量是“可观察的副作用”,优化器必须按照代码给出的顺序保持这种副作用。)
修改foo示例以产生副作用:
extern volatile int RW_A = 0;
extern volatile int RW_B = 0;
int accu = 1;
int foo(int a) { accu *= 2; return a * a; }
void bar(int x) { RW_A = x; }
int _tmain(int argc, _TCHAR* argv[])
{
bar(foo(2));
bar(foo(2));
RW_B = accu;
return 0;
}生成以下伪码:
registerA = accu;
registerA += registerA;
accu = registerA;
registerA += registerA;
registerC = 4;
accu = registerA;
RW_A = registerC;
RW_A = registerC;
RW_B = registerA;我们观察到,常见的亚表达消除仍在进行,并与副作用分离。内联和重新排序允许将副作用与“纯”部分分开。
注意,编译器读取并急切地写回accu,这是不必要的。我不确定这里的理由。
最后:
编译器不需要测试纯度。它可以识别出需要保留的副作用,然后根据自己的喜好来改变其他的副作用。
这样的优化是值得的,即使对于琐碎的函数也是如此,因为,除其他外,
堆栈内存访问的开销通常是~1周期,因为堆栈的顶部通常已经处于1级缓存中。注意,通常应该用粗体表示:它可以是“更好的”,因为读/写可能被优化掉,或者更糟,因为L1缓存的压力增加会将其他一些重要数据刷新回L2。
极限在哪里?
理论上讲,编译时间。实际上,优化器的可预测性和正确性是额外的权衡。
使用VC2008的所有测试,默认的“发布”构建优化设置。
https://stackoverflow.com/questions/12272563
复制相似问题