优化编译器是否会删除对结果将乘以零的方法的调用?

时间:2010-12-22 07:01:44

标签: optimization

假设你有一个计算成本高昂的方法Compute(p),它返回一些浮点数,另一个方法Falloff(p),它将另一个浮点数从零返回到一个。

如果您计算Falloff(p) * Compute(p),当Compute(p)返回零时,Falloff(p)仍会运行吗?或者您是否需要编写一个特殊情况来防止Compute(p)不必要地运行?

理论上,优化编译器可以确定在Falloff返回零时省略Compute对程序没有影响。但是,这有点难以测试,因为如果你有Compute输出一些调试数据来确定它是否正在运行,编译器就会知道不会因为调试信息而忽略它,导致一种薛定谔猫的情况。 / p>

我知道这个问题的安全解决方案只是添加特殊情况,但我只是好奇。

3 个答案:

答案 0 :(得分:6)

一般来说,编译器会知道函数调用可能会产生副作用(无限循环,异常等),并且不会对其进行优化。另一方面,有一些整程程序优化器可以确定函数没有副作用,因此在不使用返回值时省略它。

请注意,如果您的函数返回IEEE float并且它乘以0,则不能安全地省略函数调用,除非您可以确定它始终返回实数。如果它可以返回InfNaN,则乘以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来标记纯粹的功能(即没有副作用)。它甚至不是编译器编写者即将添加这种优化的情况,但是他们记得在最后一分钟记住了浮点陷阱:毕竟,他们甚至没有对整数进行优化。

真正的问题是,大多数编译器都不够聪明,无法考虑上下文,所以他们只能在某种程度上近视地说“我这里有乘法,一般情况下哪些优化适用于乘法?”由于在程序繁殖(比较和分支需要时间,毕竟)的情况下,优化几乎总是会严重降低性能,因此它甚至不会将其列为要考虑的优化。

由于可以考虑上下文,因此您几乎肯定必须手动添加该优化。