有几个原因。
1。编译耗时较长
对于小型甚至中型项目,这在今天很少成为问题。现代计算机非常快。如果需要五或十秒通常没关系。但对于较大的项目,它确实很重要。特别是如果构建过程没有正确设置。我记得当我试图在游戏中添加一个功能时The Battle for Wesnoth。编译花了大约十分钟。如果可以的话,很容易看出您希望将其缩短到五分钟或更少。
2。优化后的代码更难调试
它使代码更难调试的原因是调试器不会逐行运行程序。那只是一种错觉。这是一个可能存在问题的示例:
int main(void) {
char str[] = "Hello, World!";
int number_of_capital_letters = 0;
for(int i=0; i<strlen(str); i++) {
if(isupper(str[i]))
number_of_capital_letters++;
}
printf("%s\n", str);
// Outcommented for debugging reasons
// printf("%d\n", number_of_capital_letters);
}
您启动调试器并想知道为什么它不跟踪number_of_capital_letters。然后您会发现,由于您已注释掉最后一个 printf 语句,因此该变量未用于任何可观察的行为,因此优化器将您的代码更改为:
int main(void) {
puts("Hello, World!");
}
有人可能会争辩说,您只需关闭优化器即可进行调试构建。当cow is a sphere 时,这在世界上是正确的。但第三个原因是
3。有时错误只会出现在更高的优化级别。
假设您有一个庞大的代码库。当您升级编译器时,突然出现一个错误。当您删除优化时,它似乎消失了。这里有什么问题?好吧,这可能是优化器中的错误。但它也可能是代码中的一个错误,它在新版本的优化器中表现出来。很多时候,具有未定义行为的代码在经过优化编译的代码中表现不同。
那你是做什么的?您可以尝试找出错误是在优化器中还是在您的代码中。这可能是一项非常耗时的任务。让我们假设它是优化器中的一个错误。该怎么办?你可以降级你的编译器,这不是最优的,有几个原因。特别是如果它是一个开源项目。想象一下,下载源代码,然后运行构建脚本,并为找出问题所在而绞尽脑汁数小时,然后您在一些文档中看到(前提是作者记录了它),您需要特定编译器的特定版本。
让我们假设这是您的代码中的错误。理想的事情当然是修复它。但也许你没有这样做的资源。这次你还可以要求编译它的任何人使用特定编译器的某个版本。
但是,如果您可以只编辑一个 Makefile 并将 -O3 替换为 -O2,您可以清楚地看到,在时间不是无限资源的非理想世界中,有时这是一个可行的选择。如果运气不好,这样的错误可能需要一周的时间才能找到。或者更多。那是您可以在其他地方度过的时间。
以下是此类错误的示例:
#include <stdio.h>
int main(void) {
char str[] = "Hello";
str[5] = '!';
puts(str);
}
当我使用 gcc 10.2 编译时,根据优化级别,我得到了不同的结果。
没有优化:
Hello!
优化:
Hello!`@
自己试试吧:
https://godbolt.org/z/5dcKKrEW1
https://godbolt.org/z/48bz5ae1d
在这里我找到了一个论坛帖子,其中调试版本有效但未发布:https://developer.apple.com/forums/thread/15112
4。有时错误只出现在较低的优化级别。
是的,这也可能发生。在这种情况下,如果您不太关心正确性,则可以增加优化。但是,如果您确实关心,这可能是一种查找错误的方法。如果您的代码在经过优化和不经过优化的情况下都能正确运行,那么与仅经过优化编译的情况相比,它更有可能不包含将来困扰您的错误。
我没有找到可行的示例,但理论上可能可行。
int main(void) {
if(1/0) // Division by zero
puts("An error has occurred");
else
puts("Everything is fine");
}
如果编译时没有优化,很有可能会崩溃。但优化器可能会假设未定义的行为(如除以零)永远不会发生,因此它将代码优化为:
int main(void) {
puts("Everything is fine");
}
假设1/0 是某种错误检查,不太可能评估为真,因此您通常会假设程序打印“一切都很好”。在这里,优化器隐藏了一个错误。
5。优化器可能会生成一个更大的二进制文件,或者使用更多的内存。或者其他不受欢迎的东西。
这有时很重要。特别是在嵌入式系统中。通常(总是)-O0 生成非常大的代码,但您可能希望使用-Os(优化大小而不是速度)而不是-O3 来获得一个小的二进制文件。有时还可以获得更快的代码。见下文。
6。优化器可能会产生较慢的代码
是的,真的。这并不经常发生,但它可能会发生。 this question 中说明了一个相关但不等效的示例,其中编译器在优化可执行文件大小时生成的代码比速度更快。