假设你有一个计算成本高昂的方法Compute(p)
,它返回一些浮点数,另一个方法Falloff(p)
,它将另一个浮点数从零返回到一个。
如果您计算Falloff(p) * Compute(p)
,当Compute(p)
返回零时,Falloff(p)
仍会运行吗?或者您是否需要编写一个特殊情况来防止Compute(p)不必要地运行?
理论上,优化编译器可以确定在Falloff返回零时省略Compute对程序没有影响。但是,这有点难以测试,因为如果你有Compute输出一些调试数据来确定它是否正在运行,编译器就会知道不会因为调试信息而忽略它,导致一种薛定谔猫的情况。 / p>
我知道这个问题的安全解决方案只是添加特殊情况,但我只是好奇。
答案 0 :(得分:6)
一般来说,编译器会知道函数调用可能会产生副作用(无限循环,异常等),并且不会对其进行优化。另一方面,有一些整程程序优化器可以确定函数没有副作用,因此在不使用返回值时省略它。
请注意,如果您的函数返回IEEE float并且它乘以0,则不能安全地省略函数调用,除非您可以确定它始终返回实数。如果它可以返回Inf
或NaN
,则乘以0不是nop,必须执行。
答案 1 :(得分:2)
除非在编译时已知该项为0,否则不会优化任何内容。
另外,请注意分支是昂贵的,所以除非Fallout()
足够频繁地返回0,否则如果你进行明确的测试,程序会变慢,所以最好的答案是“测试它!”。
编辑:那么如果Falloff
经常返回0,那么它是否有特殊的
if (something) return 0.0f;
?如果是这样,那么一个好主意是从Compute()
有条件地致电Falloff()
。
另外,如果Falloff()
被声明为inline
,那么
if (rez = Falloff())
rez *= Compute();
可以自动完成。
答案 2 :(得分:2)
这实际上不是解决副作用的问题:毕竟,你可以用至少gcc和clang来标记纯粹的功能(即没有副作用)。它甚至不是编译器编写者即将添加这种优化的情况,但是他们记得在最后一分钟记住了浮点陷阱:毕竟,他们甚至没有对整数进行优化。
真正的问题是,大多数编译器都不够聪明,无法考虑上下文,所以他们只能在某种程度上近视地说“我这里有乘法,一般情况下哪些优化适用于乘法?”由于在程序繁殖(比较和分支需要时间,毕竟)的情况下,优化几乎总是会严重降低性能,因此它甚至不会将其列为要考虑的优化。
由于可以考虑上下文,因此您几乎肯定必须手动添加该优化。