是否允许编译器优化浮点常量乘法

Posted

技术标签:

【中文标题】是否允许编译器优化浮点常量乘法【英文标题】:Are compilers allowed to optimize floating point constant multiplication 【发布时间】:2015-01-29 12:17:47 【问题描述】:

有问题的代码如下:

float32_t f = someValueFromSomewhere;
f = f * 4;

编译器会对此进行优化吗? 根据 C 标准(如果我理解正确的话),第二个操作数必须提升为 float32_t; 所以乘法必须使用 FPU(或 fp 仿真)来完成。

理论上,该操作可以在普通硬件寄存器中完成,只需添加一个立即数(并且可能是溢出检查)。 允许编译器进行此优化吗?是否有已知的编译器这样做?如果是这样,他们是否也能认出这种表达方式

f = f * 4.0f;

这是避免关于隐式转换的静态代码检查器警告所必需的吗?

一些补充: 我知道从标准的角度来看,两条线是等效的。但显然编译器可以区分它们。所以问题是在什么时候允许优化器第一次看到代码(或更好的内部表示)。

【问题讨论】:

升级到float 是在编译期间完成的。所以表达式f = f*4f = f*4.0f 是等价的。之后它们如何编译成程序集取决于手头的编译器,但它们仍然以相同的方式编译(每个编译器)。 我会故意误解你的问题,但希望以一种启发性的方式。 只要可观察到的效果相同,编译器就可以优化它喜欢的任何东西。 如果从未在任何地方使用该值,则您显示的代码 sn-p 可能会被优化掉而不会留下任何痕迹。如果使用它,只要效果相同,编译器就可以为所欲为。我认为它可以在编译时计算 f 并用常量替换它,如果 someValueFromSomewhere 在编译时已知并且它可以静态地证明它在这些行之后永远不会改变,例如。 相关:Why doesn't a compiler optimize floating-point *2 into an exponent increment? LOAD REGX f; LOAD REGY 4; FMUL REGX, REGX, REGY (reg x = reg x * reg y) 有什么需要优化的地方? @Jens 在 x86/x87 上什么都没有。但可以通过尝试将这条简单的线推过 avr-gcc 并查看完整的输出来扩大您的视野 【参考方案1】:

不,它不起作用

仅当原始值不是次正规值(包括公共零)、不是无限或 NaN 并且结果不溢出时,将指数加 2 来代替乘以 4.0 才有效。一般来说,编译器没有这些信息。当编译器确实有这个信息的时候,就允许做这个转换,但这并不意味着它是一个好主意。

在大多数架构上,这不是一个好主意

除非您正在考虑一个特定的执行平台,在该平台上从它所在的浮点寄存器中获取 f 的值会更便宜,将其移动到通用寄存器中,添加一个常量,测试特殊情况(见上文),然后回到浮点寄存器,您可以假设所有这些步骤都比浮点乘法更昂贵。只有当浮点运算被模拟为一系列位和整数运算时,以这种方式“优化”乘以 2 的幂才有意义。

> 将 [编译器] 也识别表达式 f = f * 4.0f;

f * 4 等价于f * (float)4,因此等价于f * 4.0f。编译器可以将这些形式中的任何一种转换为与转换另一种形式相同的代码,并且任何非玩具编译器都知道它们是等效的(例如,作为constant propagation 优化传递到(float)4 的应用程序)。

【讨论】:

有趣的 OP 没有询问f * 4.0 通常会从内存中加载一些值。我不知道它们是否可以直接转移到 FPU 寄存器,但我认为转移到 CPU-Regs 或 FPU-Regs 不会产生影响(至少对 CPU 没有负面影响)。我知道升级到float32_t,但在此步骤之前可能会进行一些优化。这就是为什么如果处理 int 常量而不是 float 常量,那么自从提到的优化之后,明确的单独问题似乎更明显 @vlad_tepesch 类似地,在 chux 的评论中,编写 4.0 会强制编译器将赋值视为等同于 f = (float)((double)f * 4.0);,同样因为这是在标准),然后意识到这是等价的。出于微妙的原因,它是等效的,编译器可能无法实现。 @vlad_tepesch “表达式”(float) 4 在编译时是已知的。编译器必须处理这样的“表达式”因为这就是语言的一般定义方式。实现编译器的第一条规则是让所有情况都正确,因此任何合理的编译器都将从插入从 intfloat 的转换开始。 如果我要实现这个优化,我会基于浮点常量形式。减少不同案例的数量可以简化优化。浮点情况更普遍 - 考虑乘以 0.25 - 无论如何都必须转换为浮点。

以上是关于是否允许编译器优化浮点常量乘法的主要内容,如果未能解决你的问题,请参考以下文章

constexpr:确定性的常量优化

sse2浮点乘法

Java技术指南「编译器专题」重塑认识Java编译器的执行过程(常量优化机制)!

Java技术专题「编译器专题」重塑认识Java编译器的执行过程(常量优化机制)

常量优化机制

编译器的常量优化