Mit*_*101 14 c assembly printf gcc compiler-optimization
#include <stdio.h>
int main(void) {
int i;
scanf("%d", &i);
if(i != 30) { return(0); }
printf("i is equal to %d\n", i);
}
Run Code Online (Sandbox Code Playgroud)
看来结果字符串将始终以"i等于30",那么,为什么不GCC优化此调用与一个调用printf puts()
,或者write()
,例如?
(刚刚检查了生成的程序集,使用gcc -O3
(版本5.3.1),或者在Godbolt Compiler Explorer上)
Mat*_*lia 11
首先,问题不在于if
; 如你所见,gcc
透过它if
并设法30
直接通过printf
.
现在,gcc
确实有一些逻辑来处理特殊情况printf
(特别是,它确实优化printf("something\n")
甚至printf("%s\n", "something")
是puts("something")
),但它非常具体,并没有进一步发展; printf("Hello %s\n", "world")
例如,保持原样.更糟糕的是,上面没有尾随换行符的任何变体都保持不变,即使它们可以被转换为fputs("something", stdout)
.
我想这归结为两个主要问题:
上面两个案例是非常容易实现的模式,并且经常发生,但对于其他情况,它可能很少值得努力; 如果字符串是常量且性能很重要,程序员可以轻松地处理它 - 实际上,如果性能printf
很关键,他不应该依赖于这种优化,这可能会在格式字符串稍有变化时中断.
如果你问我,即使只是puts
上面的优化已经"追求风格点",除了人工测试用例之外,你不会真正获得任何好的表现.
当你开始走出境界时%s\n
,printf
是一个雷区,因为它对运行时环境有很强的依赖性; 特别是,许多printf
说明符(不幸的是)受到语言环境的影响,而且还有一个特定于实现的怪癖和说明符的提升(并且gcc
可以使用printf
来自glibc,musl,mingw/msvcrt,... - 并且在编译时你不能调用目标C运行时 - 在交叉编译时考虑.
我同意这个简单的%d
案例可能是安全的,但我可以理解为什么他们可能决定避免过于聪明,只在这里执行最愚蠢和最安全的优化.
对于好奇的读者,这里就是这种优化的实际执行; 正如你所看到的,该函数匹配了有限数量的非常简单的情况(除了GIMPLE之外,由于这篇描述它们的好文章已被编写,因此没有发生太大变化).顺便提一下,源实际上解释了为什么他们无法fputs
为非换行情况实现变体(stdout
在编译阶段没有简单的方法来引用全局).
现代编译器非常聪明,但不够聪明,不能用逻辑来预测输出.在这种情况下,人类程序员很容易优化这个代码,但这个任务对于机器来说太难了.事实上,在不运行程序的情况下预测程序的输出是不可能的(例如gcc).为了证明,请参阅暂停问题.
无论如何,你不希望没有输入的所有程序都被优化为多个puts()
语句,因此GCC不优化包含一个scanf()
语句的代码是完全合理的.
但是,这并不意味着编译器不能或不应该进行优化以生成更优化的执行文件.虽然无法预测所有程序的结果,但完全可能并且希望能够改进其中的许多程序.