很容易看出:
(i % 3 == 0) && (i % 5 == 0)
可以简化为:
(i % 15 == 0)
然而,考虑到海湾合作委员会的产出,即使在高优化水平下也似乎没有这样做。
是否有任何编译器进行这些优化,或者这两个测试在语义上是否相同是否有充分的理由?
编辑:回应那些说这是边缘案例的人,以下是类似的案例:
(i < 3) && (i < 5)
任何小于3的数字必须始终小于5.第二次测试是多余的。
我还想补充以下内容以回应编译器无法知道环境是否受到影响的答案......看看这段代码:
void foo(void)
{
int i;
for (i = 0; i <= 10; i++)
{
if (i > 20)
{
puts("Hi");
}
}
}
GCC使用-O2
将整个函数简化为“repz ret”。这比我说的任何事都复杂得多。
答案 0 :(得分:5)
忽略所有愚蠢的答案,声称这对编译器来说非常困难/不可能。我认为没有理由为什么会这么困难,但可能没有人想到这样做或者认为优化是非常重要的。如果您想要一个比这更好的答案,您需要在GCC错误跟踪器上报告它作为增强请求并查看开发人员的说法。
答案 1 :(得分:3)
您的示例相当简单,并且很容易压缩成单个操作。但是,像这样的声明的一般情况并不那么简单。请采取以下措施,例如:
((++i) % 3 == 0) && ((++i) % 5 == 0)
这种变化不能简单地简化为单个模数操作(我知道这个陈述有各种其他问题,但重点是当你使用除了以外的任何其他问题时问题并不那么简单一个简单的变量引用)。编译器通常不会看到您的案例只涉及简单变量,并且与一般情况不同地对其进行优化;他们尽可能保持优化的一致性和可预测性。
<强>更新强> 您添加到问题中的额外案例属于与原始代码段完全不同的优化类。它们都被优化掉了,因为它们是无法访问的代码,并且可以在编译时证明这样。您的原始问题涉及将两个操作重写为一个与原始操作唯一的等效操作。您添加的两个代码段不会重写现有代码,它们只会删除永远不会执行的代码。编译器通常非常擅长识别和删除死代码。
您正在寻求的优化是数学强度降低的一种形式。大多数编译器提供一定程度的MSR优化,但它们的详细程度将根据编译器和目标平台的功能而有所不同。例如,针对没有模数指令的CPU架构的编译器(意味着必须使用可能冗长的库函数)可以更积极地优化这些语句。如果目标CPU具有硬件模数支持,则编译器编写者可能认为两个或三个保存的指令太小而无法获得实现和测试优化改进所需的时间和精力。我在过去看到过这种情况,某些操作可以在x86 CPU的单个指令中完成(例如),但在RISC CPU上需要几十条指令。
答案 2 :(得分:1)
老实说,这是一个非常具体的案例。如果等式的任何部分发生变化,则优化将不再适用。我认为这是一个成本与收益的问题,实现这种优化的成本(编译时间的增加,维护难度的增加)超过了收益(在极少数情况下会少一些不那么快的操作)。
通常,由于精度限制和溢出的可能性,不能应用数学重构。虽然我不认为这是一个问题。